Below are the five most recent posts in my weblog. You can also see a chronological list of all posts, dating back to 1999.

The Glasgow Haskell Compiler (GHC) has support for user-supplied rewrite rules, which applied during one of the compiler optimisation stages. An example rule is

      "streamFilter fuse" forall f g xs.
          streamFilter f (streamFilter g xs) = streamFilter (f.g) xs

I spent some time today looking at these more closely.

In order for rewrite rules to be applied, optimisation needs to be enabled. This conflicts with interactive use of GHC, so you can't explore these things in GHCi. I think rewrite rules are enabled by default (with optimisation), but you can ask for them explicitly. When investigating these it's also useful to ask ghc to always recompile, otherwise you have to change the source or manually remove .hi or .o files (etc.) between invocations. A starting set of command-line options to use then, are

-O -fenable-rewrite-rules -fforce-recomp

GHC runs several compilation stages, and the source program is transformed into several different languages or language dialects as it goes. Before the phase where rewrite rules are applied, some other optimisations take place, and the source gets desugared. You can see the results of the desugaring by passing the argument -ddump-ds. Here's an example program

main = print (unStream (streamFilter (>3) (streamFilter (<10)
    (mkStream [0..20]))))

And here's what it looks like after the first pass optimisation and desugaring:

  = print
      ($fShow[] $fShowInteger)
            (let {
               ds = 3 } in
             \ ds -> > $fOrdInteger ds ds)
               (let {
                  ds = 10 } in
                \ ds -> < $fOrdInteger ds ds)
               (mkStream (enumFromTo $fEnumInteger 0 20)))))

(Note: I used -dsuppress-all and -dsuppress-uniques to improve the clarity of the above output. See Suppressing unwanted information for further details).

Those short-hand sections ((<3)) in the input program are expanded to something quite a lot longer. Out of curiosity I tried it again with plain lambdas, not sections, and the result was smaller

  = print
      ($fShow[] $fShowInteger)
            (\ x -> > $fOrdInteger x 3)
               (\ x -> < $fOrdInteger x 10)
               (mkStream (enumFromTo $fEnumInteger 0 20)))))

Rewrite rules happen after this. Once they're done (and several other passes), the program is translated into an intermediate representation called Tiny Core. This language faintly resembles the input Haskell. GHC will output the Tiny Core program if you supply the argument -ddump-simpl. Here's (most) of the program in Tiny Core (I've substituted some constants for clarity):

main  = hPutStr' stdout main1 True
main1 = $fShowInteger_$cshowList (catMaybes1 (main_go 0)) []
  = \ x ->
      case gtInteger# x 20 of {
        DEFAULT ->
          case ltInteger# x 10 of {
            DEFAULT -> main_go (plusInteger x 1);
            1# ->
              case gtInteger# x 3 of {
                DEFAULT -> main_go (plusInteger x 1);
                1# -> : (Just x) (main_go (plusInteger x 1))
        1# -> []

After Tiny Core, GHC continues to translate the program into other forms (including STG, CMM, ASM) and you can ask GHC to dump those representations too, but this is all downstream from the rewrites so not relevant to them.

The rewrite rule at the top of this blog post is never applied: It doesn't get a chance, because the function it operates on (streamFilter) is inlined by GHC. GHC can detect this in some circumstances (-Winline-rule-shadowing). You instruct GHC to report on which rules fired with -ddump-rule-firings and can see before-and-after snippets of Tiny Core for each rule applied with -ddump-rule-rewrites.

I played around with adding {-# NOINLINE functionName #-} pragmas to disable inlining various functions to try and provoke a situation where the above rule could match, but it was a losing battle: GHC's built-in optimisations are just too good. But, it's also moot: the outcome I want (the filters to be fused) is happening, it's just the built-in rewrite rules are achieving it, once striot's functions have been inlined away.


I've now completed my 4th year on my part-time PhD (a.k.a. Stage 2). I'm 2/3 of the way through.

Here's my stage 2 / year 4 report. As I've written before, this isn't graded so it might not be an example of a good one, but I did pass progression.

I had hoped to write some more blog posts about the actual content of my work before another administrative document. I spent a couple of hours drafting one a few weeks back but didn't finish it!



On the advice of many friends, I tried to use You Need A Budget. I gave it a seriously long, proper evaluation: over a year. But I just couldn't get it to work for me. I don't want to try and explain why. To be honest, those same friends who advocated for it fairly strongly, also gave me a pretty hard time for giving up on it!

Despite it not clicking for me, there are a few concepts from YNAB that I quite like:

  • "give every dollar a job". Or: Budget your entire income.
  • The jam-jar budgeting approach of carrying budget "pots" over from month to month, so you can budget a small amount towards a large thing over a long period.


Jimmy Kaplowitz suggested back in 2012 that I should take a look at GNUCash. It took me a few more years before I did. The eventual trigger point for me was organising an event where I paid for a load of things on behalf of others and needed to track who had paid me back. It excelled for that.

I've continued to use GNUCash to manage my personal money — that is, my "play money" and anything I've accumulated — but I haven't committed to it for my family finances. Practically speaking that would lock my wife out of them, which wouldn't be fair. But also because GNUCash's shortcomings (and despite its strengths, it certainly has some) mean that I don't expect I will be using it into the indefinite future, even for my personal stuff.

The most significant drawback, in my opinion, is GNUCash's support for scripting. Sometimes, there's a laborious but easily-mechanisable (in theory) task I need to perform that would be ideal to script. GNUCash has built-in scripting support using Guile — the GNU lisp/scheme dialect — but this is limited to Reports only, I don't think it can be used for a task such as "match a series of transactions using one or more filters or regular expressions, and apply a transformation to them, such as change the account to which they are posted", etc.

It also has a C library and auto-generated bindings for other languages. This has a horrible API, which is carried over into the language bindings. Documentation for the whole lot is basically non-existent too.

Plain-text accounting

For that reason I set out to find some better tools. There's a lot of interest and activity in plain-text accounting (PTA), including tools such as beancount, ledger or the Haskell re-implementation hledger. In a future post I'll write about PTA and hledger.


I wrote about budgeting nine years ago and I've been a little reluctant to write about it again: by far, it's the blog post that has attracted the most requests from people asking me to link to their blog, site, or service.

I wasn't good at budgeting then and I'm still not good at it now, although I have learned a few things in the intervening time. Those things more properly relate to accounting than budgeting (so there's the first thing: I learned the difference!). I wanted to write about some of the things I've learned since then, starting with our family's approach to pooling income.


From talking to friends about how they manage stuff, this doesn't seem to be a common approach. We pay all our income into a shared account. We agree on an amount of "play money" that we can individually spend on whatever we like, and we pay that amount to ourselves from the shared account every month. Crucially, the amount we pick is the same for each of us, irrespective of our relative incomes. All of our shared family expenses come out of the shared account.

Some of my friends, especially (exclusively) the bread-winners, find this a bit alarming. One of the things I like about it is that whichever partner earns less than the other is not disadvantaged in terms of their discretionary spending. When my wife earned less than me, and I believe structural sexism was a contributing factor to that, that impacted us both equally. When my wife was not earning a salary at all, but was doing the lion's share of bringing up our children, she has the same discretionary spend as I do. Apart from the equity of it, there's a whole class of gripes and grumbles that some of my friends have about their partner's spending habits or money management that we completely avoid.


Despite my best efforts, I often end up with a lot of branches in my git repositories, many of which need cleaning up, but even so, may which don't. Two git configuration tweaks make the output of git branch much more useful for me.

Motivational example, default git behaviour:

๐ŸŠgit branch
* develop

The default sort order is alphabetical, but that's never useful for the repositories I work in. The age of the branch is generally more useful. This particular example isn't that long, but often the number of branches can fill the screen. git can be configured to use columns for branch listings, which I think generally improves readability.

๐ŸŠgit config --global branch.sort authordate
๐ŸŠgit config --global column.branch auto


๐ŸŠgit branch
  update-for-cct-module-changes-maven-etc   signing-intent-release
  openjdk-rm-jolokia                        local-modules
  ubi8.2                                    mdrafiur-pr185-jolokia
  ubi-11-singleton-jdk                      OPENJDK-312-passwd
  ubi-1.3-mergedown                         create_override_files_in_redhat_189
  OPENJDK-159-openj9-FROM                   2021-apr-cpu-proposed
  openjdk-containers-1.9                    OPENJDK-407-dnf-modules-fonts
  inline-container-yaml                     release
  update-FROM-lines                       * develop

Older posts are available on the all posts page.