December 2022
For the last few years I’ve been tasked with bringing a big batched cocktail to holiday family gatherings. I’ve brought a few different things over the years, but the last two years I’ve settled on this modified Regent’s Punch as an ideal pre-dinner holiday dram.
Regent's Punch by
Will Duncan
Yield: 16 cups
Ingredients
- 1 1/2 cups white sugar
- lemon peels from 4 lemons
- 2 cups green tea
- 1 cup pineapple juice
- 2 cups water
- 1 1/2 cups lemon juice
- 1 cup orange juice
- 2 cups Cognac, preferably Pierre Ferrand 1840
- 1/2 cup maraschino liqueur, preferably Luxardo
- 1/2 cup Jamaican rum, preferably Hamilton Gold Jamaican Rum
- 3/4 cup Batavia arrack
- 1/2 bottle Champagne
Directions
- Prepare an oleo saccharum by combining the sugar and lemon peels in the bottom of a large punch bowl (four quarts, minimum).
- Muddle the lemon peel and sugar gently from time to time over a period of 45 minutes while preparing the fresh juices and tea.
- Once steeped, use the hot green tea to melt the oleo saccharum.
- Add to the punch bowl all of the remaining ingredients, except the Champagne and store in the refrigerator until service.
- When ready to serve, add in ice and top with Champagne.
The recipe is very flexible and scales down well, and I usually make a half recipe. In addition, I make the following modifications:
- I use fresh juice when I can. Six lemons and four oranges will cover the lemon and orange juice requirements. In addition, the lemon and orange peels go into the oleo saccharum. In a future year I will try making my own pineapple juice.
- For additional holiday flavors, I steep some caffeine-free rooibos chai in addition to or instead of the green tea.
- Cut the maraschino amount in half and replace it with allspice dram.
- I try use Smith and Cross navy-strength Jamaican rum for its robust flavor and funkiness.
- I go up a price bracket and use Pierre Ferrand Ambre instead of 1840. I find that the strong vanilla character of Ambre gives the punch a reasonable backbone.
- I have no idea where to find Batavia arrack consistently, and have had good luck substituting it with a rhum agricole.
January 2020
I’m continuing to noodle around in Clojure and needed to split a string into its component characters. If you know your string will be plain-old-ASCII, you can do:
(clojure.string/split "hello" #"")
;; ["h" "e" "l" "l" "o"]
But this gets funny if you want to split up a string that has emoji in it:
(def ghosties "👻👻👻")
(clojure.string/split ghosties #"")
;; ["?" "?" "?" "?" "?" "?"]
This is because emoji are represented by two bytes under the hood, called a surrogate pair. When you split up a string using the empty regex, it spits it on each byte not on each character. This is a problem in Javascript as well.
To solve this problem in Clojure, we need to reach into the Java APIs for manipulating strings and characters:
;; For a given integer codepoint, return the string representation of it
(defn codepoint-to-str [cpt]
(-> (StringBuilder.)
(.appendCodePoint cpt)
str))
;; For a given string, split it into codepoints and generate a string
;; for each individual code point.
(defn to-string-array [s]
(map
codepoint-to-str
(iterator-seq (.iterator (.codePoints s)))))
(to-string-array ghosties)
;; ("👻" "👻" "👻")
A fun software challenge has been floating around my social network this holiday season: given a list of gifts, print out the lyrics to The Twelve Days of Christmas. I’ve been itching to write Clojure again lately, and so I decided to use this as an opportunity to get my head back in that world a bit. I wrote the code using a REPL, because that’s a big part of my enjoyment of Clojure.
There are some interesting bits of the challenge that I wanted to solve in a clever way. First, the song uses both cardinal numbers (one, two, three) and ordinal numbers (first, second, third). The simplest way to implement this is to just list out the first twelve cardinals and first twelve ordinals, but I thought it would be fun to use Clojure’s dynamism to pull in a dependency from the Leiningen REPL.
To do that, I first needed to add the lein-exec
plugin to my ~/.lein/profiles.clj
, which gives access to Leiningen’s pomegranate library.
{:user {:plugins [[lein-exec "0.3.4"]]}}
This gives me access to deps
, a function that lets me pull dependencies down from Maven Central. There’s a package by IBM called icu4j
which has features for printing cardinal and ordinal numbers:
(use '[leiningen.exec :only (deps)])
(deps '[[com.ibm.icu/icu4j "65.1"]])
(import 'java.util.Locale 'com.ibm.icu.text.RuleBasedNumberFormat)
;; set up fns for formatting numbers
(def rbnf (RuleBasedNumberFormat. Locale/ENGLISH RuleBasedNumberFormat/SPELLOUT))
(defn cardinal [n] (.format rbnf n "%spellout-cardinal"))
(defn ordinal [n] (.format rbnf (long n) "%spellout-ordinal"))
I now have cardinal
and ordinal
functions, which can be called with a number to get the cardinal/ordinal string of that number.
Next, I need to render a list of gifts using cardinal numbers—four calling birds, three French hens, etc—and decided to use Clojure’s dispatch-on-arity and recursion to print out all the gifts passed to it. A multimethod may be more appropriate here, using the length of the passed vector rather than using arity like this. Nevertheless:
(defn gift-lines
([gift] (str " a " gift)) ;; fn for one gift (used on the first day)
([gift-1 gift-2] ;; fn for two gifts (2nd day and later)
(str " " (cardinal 2) " " gift-1 "\n and a " gift-2))
([gift-1 gift-2 & gifts] ;; fn for many gifts (3rd day and later)
(str " "
(cardinal (+ 2 (count gifts))) " " gift-1 "\n"
(apply gift-lines gift-2 gifts))))
Now, a function that renders a whole verse, which is just adding a prefix of “On the [ordinal] day of Christmas…” to the previous function, then rendering the list of gifts in reverse.
(defn verse [gifts] ;; generate a verse (day) of the song
(str
"On the " (ordinal (count gifts)) " day of Christmas, my true love gave to me\n"
(apply gift-lines (reverse gifts))))
And lastly, a function that prints out the whole song, which is a loop that consumes progressively more and more of the list of gifts until it runs out. I could call this function with any number or any list of gifts, and it should absorb the difference gracefully.
(defn song [gifts] ;; print out all verses of the song for the list of gifts
(doseq [day (range 1 (inc (count gifts)))]
(println (verse (take day gifts)))
(println "")))
Calling the song
function:
(song [
"partridge in a pear tree"
"turtle doves"
"French hens"
"calling birds"
"gold rings"
"geese a-laying"
"swans a-swimming"
"maids a-milking"
"ladies dancing"
"lords a-leaping"
"pipers piping"
"drummers drumming"])
Outputs:
On the first day of Christmas, my true love gave to me
a partridge in a pear tree
On the second day of Christmas, my true love gave to me
two turtle doves
and a partridge in a pear tree
...
On the twelfth day of Christmas, my true love gave to me
twelve drummers drumming
eleven pipers piping
ten lords a-leaping
nine ladies dancing
eight maids a-milking
seven swans a-swimming
six geese a-laying
five gold rings
four calling birds
three French hens
two turtle doves
and a partridge in a pear tree
A fun little challenge, and a fun thing to do with a Clojure REPL.
June 2018
The first new Zoe Keating album since 2010. From her blog a month ago:
As many of you know, four years ago my world exploded and disintegrated: my husband Jeff was diagnosed with terminal cancer, he died 8 months later and I became an only parent.
…
The act of finishing a single song made it seem possible for me to make another one, which I did after I gave the talk. And then I made another…
One theme running through all my music is the feeling of getting outside of things to get a bigger vista. I’m often looking for the musical equivalent of a bird’s eye view. It has been hard to get that kind of perspective of my life for the last few years. And frankly, it’s hard to imagine making something big, like an album, when you’ve lost your confidence, which I certainly had.
December 2017
He sat in his suit, candy-striped, and poured the bag of beans onto the table.
“There’s a flavour here, the red one, that you’ll really like, and another, the blue one, that you’ll really hate. There are two that are good, one bad and four you’ll be indifferent to. You have to eat them all, and usually in handfuls, not one flavour at a time. That’s your life.”
“And after I Commence there are more red ones and fewer blue ones?”
“Oh no. There are just fewer bland ones, and you never get to the bottom of the bag.”
— from the musical HE SAID HE COULD
November 2017
This post originally appeared on the Faraday Blog
At work, we use redux-saga in our client-side webapp because it offers a robust and testable set of tools for managing complex and asynchronous effects within Redux apps. I recently upgraded our version of redux-saga from an embarrassingly old version to the latest, in part to take advantage of a new (but largely undocumented) feature: context.
The problem
I want to be able to write sagas without having to implicitly import any statically-declared singletons. The reason for that is simple: if I’m testing the saga that creates new users, I don’t want to drag along the API layer or the router instance.
There are also a few modules in the codebase that only work in a browser and will fail loudly when I try to run them in mocha. If one of these browser-only modules ends up in an import chain, suddenly my tests start failing for bad reasons. At worst, I have to rethink my testing strategy to include a mock browser environment. I’m lazy and hate testing, and I think mocks are a gross awful code smell that should be avoided at all costs.
The solution
One of redux-saga’s greatest features is that I can test a saga’s behavior without actually executing that behavior, or even really knowing much about the details of how that behavior alters the world. It it should be possible to apply that to dependencies as well. And it is possible in redux-saga 0.15.0 and later using context.
Right now, there are very limited docs for context, but acts as shared value across all sagas that can be read with the getContext
effect and written to by the setContext
effect. It can also be set when the saga middleware is created by passing a context object as configuration.
Example
Let’s say we’re writing an app that fetches game inventory data from the server using some kind of authentication. We don’t want tokens and authentication to bleed into all our sagas, so we wrap it up in a nice singleton API service:
class ApiService {
getInventory = () => {
return fetch('/api/inventory', {
headers: {
Authorization: `Bearer ${this.token}`
}
}).then(res => res.json())
}
}
const api = new ApiService()
api.token = localStorage.token
export default api
Without context, our saga would probably statically import the API singleton:
import { call } from 'redux-saga/effects'
import api from './api'
export function * fetchInventorySaga () {
const inventory = yield call(api.getInventory)
// Do something with the inventory data...
}
All is well until we try to test it in nodejs/mocha:
import { fetchInventorySaga } from '../src/sagas/inventorySaga.js
// ReferenceError: localStorage is not defined
There is no localStorage in the nodejs global context. We can either pull in a testing harness to change how import api from './api'
is resolved, attempt to run the tests in a browser, or roll our own late-binding mechanism so that you don’t need to import API and can pass the API instance in at runtime.
We need to solve the same problem for fetch
, because that’s also absent in nodejs.
Or, we could use context. Our saga changes only a little bit:
import { call, getContext } from 'redux-saga/effects'
// No more API import:
// import api from './api'
export function * fetchInventorySaga () {
// Get the api value out of the shared context:
const api = yield getContext('api')
const inventory = yield call(api.getInventory)
// Do something with the inventory data...
}
We can now test the getContext
effect just like we would any other redux-saga effect, and we can insert a mock value into fetchInventorySaga
at test-time if we need to.
Setting up context in the main application is very straightforward. When you’re creating your saga middleware:
import createSagaMiddleware from 'redux-saga'
import api from './api'
const saga = createSagaMiddleware({
context: {
api // our singleton API value
}
})
Being able to late-bind singleton values like this has been enormously helpful writing robust tests in a complex codebase. I’ll be steadily migrating the application code to use getContext
more frequently, now that I have it as an option.
August 2016
From an article in Aeon by Huw Price, likening energy sources to clean water. We need to get away from fossil fuels, but there’s an enormous reputation cost for pursuing cold fusion:
We are like a thirsty town, desperate for a new water supply. What we drink now is slowly killing us. We know that there’s an abundant supply of clean, cheap water, trapped behind the dam. The problem is to find a way to tap it. A couple of engineers thought that they had found a way 25 years ago, but they couldn’t make it work reliably, and the profession turned against them. Since then, there’s been a big reputation cost to any engineer who takes up the issue.
If low-energy nuclear reactions are possible and relatively simple to construct, they could solve our energy problems instantly. There’s no reason in principle that these reactions couldn’t exist, but finding them and understanding them well enough to build energy sources around them might be extraordinarily difficult. But the practical scientific research community is so turned against cold fusion that any breakthroughs that might be made (or have been made) are ignored.
March 2016
Twenty percent of the company had just been laid off, a number of scandals had just unfolded, there were reorgs and more reorgs. This monumental failure could be directly traced back to the globalization of CCP’s development. Yet here I was reading about what a great model it was in the seminal work about Agile game development.
I played Eve Online during the years that CCP’s business and technical priorities became more and more distant from the desires of their fanbase. It doesn’t surprise me that Scrum was a contributing factor to their early-2010’s inability to deliver features, clean up technical debt, or communicate their vision effectively.
February 2016
Building an app using Redux and securing it using JWT:
In this tutorial, we’ll explore some of the principles of Redux and look at how to build a simple React application with it. We’ll focus on how to add authentication to our Redux app by allowing users to log in and access secure endpoints from a NodeJS API with JWT authentication.
Not just a decent breakdown on how to add login/logout to a Redux app, but also a pretty good overview of Redux.
Most of my job is building interfaces for humans to interact with a complex data model. A lot of the time, that’s laying out buttons inside a window, showing the right number or text at the right time, or simplifying a firehose of data down to a nice visualization.
User interfaces aren’t just buttons and boxes, though. A user interface is a way to make a complicated thing simpler to manipulate. The way an idea is described also a user interface for thinking about it.
The more incidental complexity it takes to describe an idea, the more difficult it is to manipulate the idea in your head. Here’s a fairly simple problem expressed by a great Persian mathematician al-Khwārizmī from the tenth century:1
What is the square which when taken with ten of its roots will give a sum of thirty nine? Now the roots in the problem before us are ten. Therefore take five, which multiplied by itself gives twenty five, and amount you add to thirty nine to give sixty four. Having taken the square root of this which is eight, subtract from this half the roots, five leaving three. The number three represents one root of this square, which itself, of course, is nine. Nine therefore gives the square.
The above passage is extremely difficult to understand. A senior in high school might now represent this problem and its solution as:
x^2 + 10x = 39
x^2 - 3x + 13x - 39 = 0
(x-3) * (x+13) = 0
x = {3,-13}
(al-Khwārizmī missed the second solution of -13)
There are advantages to both “interfaces” of this math problem: The natural language version requires no special knowledge of any special notation. A person with no knowledge of mathematics, but a knowledge of language, could work their way through this problem with a dictionary.
The compact mathematical notation has significantly more advantages. Once you get past the learning curve of the concise notation, it becomes much easier to symbolically manipulate the problem. Not only is it easier to solve, it’s easy to apply the same solution to many possible problems.
It’s also possible to build higher-order concepts on top of a concise notation. Calculus is a natural byproduct of concise mathematical notation; it was discovered concurrently by both Liebniz and Newton about 15 after the vocabulary of mathematic notation had settled.1
The right abstraction/interface can produce higher-order insights. Fabricating the right kind of interfaces is critical to further innovations. This can be at the expense of easy learnability, which is a problem I don’t know how to solve (or if it even needs to be solved).
A big problem with conveying a statement of belief is that the actual outline of that belief is fractal in nature. For several months I’ve been working on a piece that tries to describe why I think agile software development is bad for everyone involved in it. But that’s not completely accurate–really, I think the agile manifesto is good, but the practices that have grown up around it are poisonous to a business that adopts it. And even then, some agile practices are valuable, and it’s the constellation of ideas that make up scrum that are so toxic.
As I try to get at the details of my position, clearly explaining it becomes much more difficult. If we imagine a constellation of opinions as a physical territory, it falls victim to the coastline paradox. The alternative is to run roughshod over the contours of what I actually think in service of a useful approximation.
But run roughshod I will. I’ve become paralyzed by nuance, and it’s not helping this particular project.
August 2015
Scott Alexander on a process I’ve been thinking about a lot lately: Accuse a fringe group of some negative feature that at least some members manifest, and do it enough, they’ll slowly turn into that negative caricature as the more reasonable ones flee toward the mainstream:
There’s a term in psychoanalysis, “projective identification”. It means accusing someone of being something, in a way that actually turns them into that thing. For example, if you keep accusing your (perfectly innocent) partner of always being angry and suspicious of you, eventually your partner’s going to get tired of this and become angry, and maybe suspicious that something is up.
Declaring a group toxic has much the same effect. The average group has everyone from well-connected reasonable establishment members to average Joes to horrifying loonies. Once the group starts losing prestige, it’s the establishment members who are the first to bail; they need to protect their establishment credentials, and being part of a toxic group no longer fits that bill. The average Joes are now isolated, holding an opinion with no support among experts and trend-setters, so they slowly become uncomfortable and flake away as well. Now there are just the horrifying loonies, who, freed from the stabilizing influence of the upper orders, are able to up their game and be even loonier and more horrifying. Whatever accusation was leveled against the group to begin with is now almost certainly true.
[…]
People like to talk a lot about “dehumanizing” other people, and there’s some debate over exactly what that entails. Me, I’ve always thought of it the same was as Aristotle: man is the rational animal. To dehumanize them is to say their ideas don’t count, they can’t be reasoned with, they no longer have a place at the table of rational discussion. And in a whole lot of Internet arguments, doing that to a whole group of people seems to be the explicit goal.
[edit: apparently I’m reading Scott Alexander a lot. I promise that I’m working on longform pieces that have only coincidental similarities to his topics of interest]
Scott Alexander on the idea of a “dualized” field, where opportunities are few on the ground, but the rewards for “making it” are huge. This principle applies to gangs, colleges, and medicine:
This concept applies much more broadly than just drugs and colleges. I sometimes compare my own career path, medicine, to that of my friends in computer programming. Medicine is very clearly dual – of the millions of pre-med students, some become doctors and at that moment have an almost-guaranteed good career, others can’t make it to that MD and have no relevant whatsoever in the industry. Computer science is very clearly non-dual; if you’re a crappy programmer, you’ll get a crappy job at a crappy company; if you’re a slightly better programmer, you’ll get a slightly better job at a slightly better company; if you’re a great programmer, you’ll get a great job at a great company (ideally). There’s no single bottleneck in computer programming where if you pass you’re set for life but if you fail you might as well find some other career path.
I have more thoughts coming on this, but I believe Agile (and Scrum in particular) causes dualization in engineering departments that adopt it. It collapses the number of different ways that engineers can measure their own success relative to their peers–Bob might be into databases, while George likes type theory. In Agile they’re just team members that rise and fall–or more accurately, don’t go anywhere–together. At the same time, the system forces them into a generic engineering role that ignores their specialization and preferences.
…[D]ualized fields are a lot more likely to become politicized. The limited high-tier positions are seen as spoils to be distributed, in contrast to the non-dual fields where good jobs are seen as opportunities to attract the most useful and skilled people.
Yup.
July 2015
One of the things I’ve been using a lot lately is Docker. I don’t pretend to be an expert, or even be very enthusiastic about it, but there’s a lot of interesting tooling being built up around it. docker-compose lets you put together large-ish systems using a configuration file, and deploy those configuration files to remote clusters. Working within Docker also means that your development environment closely resembles production, which is a boon.
The other thing I’ve been playing with in my spare time is Figwheel. The ability to live-code user interfaces without losing application state is incredible, and being able to force the application into particular states makes it really easy to test unusual edge cases.
Using Docker and Figwheel together is a bit fiddly, as I have discovered, so I’ve written down the magic incantations to save others (and my future self) the time it takes to figure it out.
It should be easy to retrofit these changes onto an existing project, but I’m going to start with a fresh project here, becasue I think it will illustrate the changes that need to happen more clearly:
lein new figwheel dockerwheel
Now that we have a new project called dockerwheel
, we need a Dockerfile. This will be our recipe for plugging into the Docker infrastructure:
FROM clojure
WORKDIR /clj
ADD . /clj
VOLUME /clj
# 3449 is default http and websocket port that figwheel uses to communicate
EXPOSE 3449
# 7888 is the default nrepl port
EXPOSE 7888
RUN lein deps
RUN lein cljsbuild once
CMD lein figwheel
# This command will start figwheel and open a repl socket
# This would need to change for a live environment -- you wouldn't want
# the figwheel socket open in production, but I'll leave that bit as an
# exercise for the reader.
There are also some changes that need to be made to the project.clj
configs to get the hostnames/IPs to line up for the Dokcer environment:
;; project.clj
:cljsbuild {
:builds [{:id "dev"
:source-paths ["src"]
:figwheel { :on-jsload "dockerwheel.core/on-js-reload"
:websocket-host "192.168.99.100"} ;; <--
;; This IP address needs to be the IP to your docker
;; container, otherwise the websocket will attempt to
;; connect to localhost:3449
;; AND:
:figwheel {:css-dirs ["resources/public/css"] ;; leave this bit alone
:nrepl-host "0.0.0.0"
;; nrepl only accepts connections from certain IP addresses
;; as a secuity measure. Setting this to "0.0.0.0" lets incoming
;; REPL requests access it from any IP address.
:nrepl-port 7888
;; Nrepl doesn't start if you don't give it a port in the configs
:repl false }
;; Finally, don't start a REPL when the container is started,
;; instead, we want to allow remote connections
Now you can build and start the container:
docker build -f Dockerfile -t dockerwheel .
# this builds the container, it can take a while to pull all the dependencies
docker run -p 3449:3449 -p 7888:7888 -v `pwd`:/clj -it --rm \
--name dockerwheel dockerwheel
# start the container and wire up the relevant ports
Now you can go to your container’s IP (in this example 192.168.99.100
) at port 3449 and see the starter figwheel app. If you want to connect a REPL, hit that same IP at port 7888.
May 2015
I’ve been meaning to write another blog post for a while now, but life has conspired against me in some of the most exciting and brutal ways possible.
About a month after I posted the last link on Angular 2.0, I gave notice to my former employer. In January, I started at Faraday, a data-driven marketing startup that uses machine learning to help companies locate and contact likely customers. It’s a really exciting place to work; smart people, cool technology, moving fast and a lot of autonomy. Somewhere, I have a draft looking back at the four years I worked for Dealer, and the months I’ve worked for Faraday; it never got published, and probably doesn’t apply anymore.
On Feburary 2nd, my stepfather died suddenly of a heart attack. He was never a father figure to me, but he was a good friend and, in many ways, the kind of person I aspire to be. His death was devastating, to me and my mother. Much of my free time in the last few months has been helping my mother through this crisis.
In the middle of February, my grandfather–my mother’s father–died. He was nearly 100, and his death was somewhat anticipated. Nevertheless, it’s difficult to work through many deaths in the family so close together.
In the beginning of May, the whole Faraday team got together for a week and to get our product ready for a public launch. Hundreds of person-hours were spent that week finishing all the bits that had been put off for months; we polished the parts of the app that weren’t working well, we redesigned the login and signup flow, relaunched the website, built an automated tour of the app, rebuilt parts of our campaign-construction process, and rethought all the indexes in our giant geospatial database.
On May 11, we soft-launched our webapp. Anyone can sign up for an account and explore a nation of demographic data. I encourage you to do so at app.faraday.io/signup.
In a couple days, I hope to finish a technical blog post about using Figwheel with Docker.
October 2014
Joel Spolsky, writing about Netscape almsot 15 years ago:
When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work.
You are throwing away your market leadership. You are giving a gift of two or three years to your competitors, and believe me, that is a long time in software years.
You are putting yourself in an extremely dangerous position where you will be shipping an old version of the code for several years, completely unable to make any strategic changes or react to new features that the market demands, because you don’t have shippable code. You might as well just close for business for the duration.
Not only is Angular 2.0 a complete rewrite, they anncounced the death of 1.x a year before 2.0 will even be ready. This seems like a really terrible idea.
Developers that are used to the incumbent look and feel of Angular are in {for a} few surprises. As InfoQ’s David Iffland comments, the framework will be “drastically different looking”, meaning users will need to get to grips with a new kind of architecture. It’s also been confirmed that there will be no migration path from Angular 1.X to 2.0.
It really does look like a new framework, rather than an incremental improvement over Angular 1.x. Also, Angular 1.x is going into maintenance mode:
One thing that is changing, however, is our support for new features and breaking changes in Angular 1.x. With a few exceptions, these will be deferred to 2.0, and will be better addressed with the new design. We’ll still be reviewing PRs and responding to issues but our focus with 1.x will be on stability, security, performance, rather than features.
Once 2.0 is released, the support timeline for 1.x bugfixes is only going to be 2 years. To me, it doesn’t make sense to be writing a lot of new Angular code for a while. But at least the new framework is going to be awesome, right?
According to AngularJS founder Miško Hevery, the goal of Angular 2.0 is to completely overhaul the development experience. One means of doing this is Google’s new programming language AtScript, which can be employed for developing Angular applications.
To steal a John Gruberism, good luck with that.
What follows is the first entry in what I imagine will be a short series about Clojure, all from the perspective of a someone who writes a lot of Javascript at his day job. This is intended to be a simplified introduction to Clojure. It will become a short presentation to interested engineers where I work. I’ve deliberately simplified and omitted some detail that would be covered by a more-comprehensive resource.
I’ve become a bit infamous among my peers for evangelizing Clojure and the unfamiliar concepts it makes use of. I’m especially interested in ideas that can be ported to more-familiar languages because I believe they make it easier to think about complex software. I believe the easiest way to learn those concepts is to become at least passingly familiar with Clojure.
Clojure can be an intimidating beast from a distance, but up-close it’s not nearly as terrifying. A recent comment by a coworker is summarized by, “Clojure seems really powerful, but when I look at that code I’m totally lost.” I believe this sentiment is rooted in Clojure’s unfamiliar syntax, but the language described by that syntax isn’t very much different from Javascript. Brenden Eich based a lot of Javascript on Smalltalk, which was itself inspired by Lisp. Clojure is a Lisp dialect, so the two languages are distant cousins of one another; while the syntaxes of each language might be wildly different, many of the underlying ideas are the same.
A quick language primer
There are two rules for translating between Javascript and Clojure. The first rule is that parenthesis begin to the left of the function name:
capitalize("hello world") // => "Hello World"
(capitalize "hello world") ;; => "Hello World"
You can see that the function named capitalize
is enclosed by parenthesis, rather than followed by them.
The second rule is that everything is a functional expression. Even mathematical operators are defined as functions, and are enclosed by parenthesis:
2 * 4 // => 8
(* 4 2) ;; => 8
2 * 4 + 1 // => 9
(+ (* 2 4) 1) ;; => 9
This looks awkward at first, but has the advantage of never being ambiguous about operator ordering.
Clojure has a bunch of built-in literals to describe different types of data, almost all of them should be familiar from Javascript:
true
, false
, nil
- The values true, false, and nothing are pretty simple
1000
, 1.25
- Numbers look the way you’d expect 1
"Hello world"
- So do strings
:nietzsche
- This is a new one. This is called a keyword, and it’s a placeholder value that always means itself. It’s similar to the Symbol type in Ruby, or a Java Enum value. It provides fast equality checking and has a few other uses throughout the language.
[ :oranges, :bananas, :pineapples ]
- This is a vector of keywords. A vector is basically an array.
{ :name "Rich Hickey", :employer "Cognitect" }
- This is a map. Note that the keys here are keywords, which aren’t strings. Maps in Clojure are like Java-style maps, in that the keys can be of any type.
#{ :oranges, :bananas }
- This is a set. It’s like a vector, but has only unique items.
(fibonacci 5)
The last peculiar thing about Clojure is that it doesn’t reserve very many characters for syntax purposes. The following are all valid names in Clojure: number?
, transact!
, assoc-in
, clj->json
, *app-state*
, >!
.
Why learn Clojure?
“Clojure feels like a general-purpose language beamed back from the near future.” This is how the book Programming Clojure describes the language. Clojure has a constellation of features that will help you build better programs:
The most striking feature to newcomers to the language is how expressive it is. A Clojure program can easily be half as long as the equivalent in other languages, and often even smaller than that. Once you’re comfortable with the standard library, it will be hard to go back to more-verbose languages. Here are two snippets in Javascript that check if an array contains only numbers:
// Naive Javascript:
function areAllNumbers(collection) {
for(var i = 0; i < collection.length; i++) {
if(typeof collection[i] !== 'number') {
return false;
}
}
return true;
}
// Or, using Javascript's collection methods:
collection.every(function(item) { return typeof item === 'number'; });
The following examples are the same algorithms in Clojure; Even if you’re unfamiliar with the language, it should be possible to see the “bones” of what’s going on:
;; Naive Clojure:
(defn all-numbers? [coll]
(cond
(empty? coll) true
(number? (first coll)) (recur (rest coll))
:else false))
;; And using the Clojure collections library:
(every? number? collection)
Javascript stands out as a very expressive language, and yet the Clojure samples remain significantly smaller. This disparity is only magnified as programs become more complex.
There is an important distinction between conciseness and terseness. Minification or creative “code golf” can make the source code smaller, but at the cost of understandability. This is what I mean by terse. Conciseness is about being able to express an idea intelligibly in a small space, such that it can be composed easily into higher-level concepts.
The next obvious feature is that it’s a functional language, which can be a tough thing to get used to. Much of Javascript is calling functions attached to objects to produce a new state. In Clojure, you call functions against values to produce a new value. If you want to append to an array in Javascript, you use the concat
method with the value you want to add, in Clojure you call the conj
(short for “conjoin”) with the collection and the value you want to add.2
[1, 2, 3].concat(4); // => [1, 2, 3, 4]
(conj [1, 2, 3] 4) ;; => [1, 2, 3, 4]
This is also a good time to bring up the excellent standard library Clojure has. Rich Hickey, the author of Clojure, is allergic to re-implementing the same functions for different data types, and the standard library reflects this.
The Clojure standard library is large, but can be used across many data types. The biggest beneficiaries of this approach are collections. Every collection in the language can use the functions that operate on sequences (including strings and maps). The values of the collections are intelligibly coerced into a shared representation that can be easily acted upon:
(first [ 1 2 3 ]) ;; => [ 1 ]
(first { :foo 1 :bar 2 }) ;; => [ :foo 1 ]
(first "Hello") ;; => \H
(first #{ :red :green }) ;; => :red
I believe most-important feature of Clojure to understand is immutability. In Javascript, strings are immutable—there’s nothing you can do to a string that won’t create a new string as a result. If you call .toUpperCase()
on a string, it will return a new copy of that string with all the letters in upper-case, rather than modifying the string in place. In Clojure, all data behaves in this way; If you want to add an item to a vector, you call conj
, which will return a new vector with the item added.
Why is this important? There are a few reasons: It means that the functions you write will usually be pure (have no side effects), which will make them easier to test and reason about. It makes sharing data throughout your program extremely safe, because distant parts of your application can’t alter the states of each other accidentally.
For example, there’s a bug in the following code:
var phoneNumbersPromise = getPhoneNumbers('george');
phoneNumbersPromise.then(function(phoneNumbers) {
phoneNumbers.forEach(function(it) {
it.type = normalizeNumberType(it);
});
renderAllPhoneNumbers(phoneNumbers);
});
phoneNumbersPromise.then(function(phoneNumbers) {
var hasHomeNumber = phoneNumbers.some(function(n) {
return n.type === 'HOME'
});
if(hasHomeNumber) renderHomePhoneNumber(phoneNumbers);
});
The behavior of one promise block depends on the behavior of the other. It doesn’t matter what normalizeNumberType
does, it affects the value of the phone number in a way that the second resolver will succeed or fail depending on execution order. Because we’re dealing with asynchronous code, execution order may be non-deterministic. These two resolvers might be separated by tens of thousands of lines of code, or may have been added at different moments during development.
If the value returned by the phone number promise were immutable, this bug could not exist.
In addition to the above major features, you get a bunch of pretty awesome minor ones:
- Live coding
- Managed CSP
- Homoiconic code
- Macros
- ClojureScript
- Easy testability
- JVM / Javascript interop
- Value destructuring
- Ad-hoc Polymorphism
- Lazy and Infinite Sequences
I believe there is value in learning new languages with unfamiliar ideas. Every programming language that’s made it to production has been a successful attempt at solving a problem in software. The ideas encoded in these languages are often portable to other languages, or can be an introduction into new ways of thinking about a particular set of problems. I believe that the concepts embedded in Clojure are incredibly useful, and they will make you a better programmer in whatever language you choose to write in.
If you’re looking to get started in Clojure, I highly recommend the book Programming Clojure by Stuart Halloway. From there, you can either try out some practical Clojure with the Basic Om Tutorial, which covers building React-style UIs in ClojureScript, or you can try out the challenges on CodeWars, which has a large-and-growing collection of Clojure challenges.
September 2014
A few days ago, the development team behind YUI announced that development on the project would be halted. I didn’t think I had anything to say about it—I assumed that I would feel some sadness for a few weeks or months until I moved onto whatever is next. But it’s been a week, and I think I’m itching for something to say.
I’ve also been looking for a reason to start blogging again, and this is a good a reason as any. In the wake of the “death” of YUI, what now? What’s good out there? What are the bad ideas? I wonder if there’s anything I can contribute to the conversation.
The world of JS has changed a lot in the last five years. The old-and-busted IE world is fading and the future looks like a bright forest of evergreen browsers. Node.js came out of left field; it now completely dominates the frontend build-tooling world and has a growing place as the frontend of the backend. There are now enough popular compile-to-JS languages that whole communities have sprouted up around each. It’s now possible to compile a C++ game engine to something that can run on the browser. The modern web is a weird place, I hope to make a little bit of sense out of it in the process.
I suspect that I’ll be writing about three different topics in the coming months:
- Exploring how to build large applications using new frontend technologies.
- Demonstrations of interesting new or “fringe” programming constructs
- Looking at the behavior and implications of language features
Before I start looking forward, I want to begin by looking back.
A retrospective on YUI
I’ve been building applications in YUI for my entire professional career. YUI2 was the first library I picked for ajax, animations, and widgets. I tracked the early development of YUI3 with great interest. Learning YUI3 threw me into entrprise-quality javascript with composable primitives, great documentation, comprehensive testing, and readable source code.
YUI was never a very sexy tool to work with. For most of the last 5 years, it was owned by Yahoo, which despite its great in-house talent, doesn’t project an aura of developer credibility, especially over the last few years. The YUI branding was boring and didn’t clearly advertise what made it special. The value of the framework was also hard to articulate to fellow UI developers; In 2010 extensibility, composibility, and maintainability took a back seat to simple APIs and fast DOM mutation. For almost everyone, the features of the library appeared monolithic and over-engineered. I believe history has validated YUI’s ideas, though—in 2014, most of the ideas first pioneered by YUI exist in current client-side frameworks.
To its fans, YUI felt like being in a near-future world where browser inconsistencies were gone, the event API was robust and sane, modules could be isolated and tested, and building applications with complicated state wasn’t a nightmare of DOM smashing. If a component didn’t do exactly what you wanted it to, almost anything could be extended and modified. Compared to black-box jQuery plugins that were its contemporary, YUI is a revelation in composability.
The community was similarly execellent. I used to monitor the message board, then the mailing list, and still spend much of my day lurking on IRC. I feel strongly that the quality of a community can be measured by the responses to open-ended design questions (“What’s the best way of doing x?”). There was always a constructive discussion about the most-maintainable way to achive a certain goal, even if it wasn’t in the “YUI happy path”. Asking a question about a new feature like promises or application routing was met with helpful examples, usually by the designers of those modules.
Unlike many modern JS frameworks, YUI isn’t “opinionated”1 about application structure or design. It makes a few recommendations, but the components were loosely-coupled and could be used in many arrangements. Prior to the announcement, there was discussion of breaking the framework up into ES6 modules that could be used outside the YUI context with limited dependencies. This would have been a significant-but-worthwhile project.
There’s still a lot of code that I’ll be working with that sits on top of YUI, but has been marked for refactoring to Angular2 since late last year. Eventually, the dependency will be removed entirely, and I’ll have few enough reasons to hit up the documentation. Still, I wouldn’t be the developer I am today without the experience I’ve had with YUI; the great ideas embedded in it will inform my decisions about writing software for a long time.