quanttype
https://quanttype.net/
Recent content on quanttypeHugo -- gohugo.ioen-usSoftware for myself
https://quanttype.net/p/software-for-myself/
Sun, 15 Mar 2026 00:00:00 +0000https://quanttype.net/p/software-for-myself/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaa3_hu_dd2327c69cb0042c.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaa3.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>Thanks to the coding agents, it’s easier than ever to create small pieces of software.</p>
<p>I’ve been creating small apps, tools, and toys for myself.
Last week I posted about <a href="https://quanttype.net/p/goblin-mode/">Goblin Mode</a>, my tool for spinning up development VMs.
Let’s take a look at what else I’ve made!</p>
<h2 id="small-web-apps">Small web apps</h2>
<p>I made a <a href="https://photos.smallrapids.net/">photo gallery</a> for sharing my photos online.
The app is called <a href="https://github.com/miikka/kuvasivu/"><em>kuvasivu</em></a> (Finnish for “picture page”) and it’s implemented in Rust.
I’m self-hosting it on a Hetzner virtual server.</p>
<p>I’ve been meaning to put more of photos online, but there hasn’t been an obvious place for them.
There are dozens of decent photo gallery projects and services but it felt easier to just prompt one into existence and self-host it.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>I also made a web app for scheduling meetings with my friends.
You create a poll with a few dates as options and everyone fills in their availability.
If anyone still remembers Doodle, it’s like that but it does not have ads.
It’s called <a href="https://github.com/miikka/beet-scheduler"><em>beet-scheduler</em></a> and it, too, is implemented in Rust.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/software-for-myself/beet-scheduler.png"
/>
</picture>
</figure>
<p>Again, there are plenty of alternatives out there.
You could use <a href="https://tapaaminen.net/en">Tapaaminen.net</a> (a service) for example.
It’s probably AI-free.</p>
<p>Also, I made a dashboard that shows how often I climb and run.
I record my runs on <a href="https://smashrun.com/">SmashRun</a> and my climbs in a spreadsheet.
The dashboard downloads the data directly from them.
I like to use it to check that I’m exercising the Goldilocks amount.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/software-for-myself/liikunta.png"
/>
</picture>
</figure>
<h2 id="small-games">Small games</h2>
<p>The coding agents are pretty good at one-shotting small games.
I like to try out new models by asking them to create <a href="https://en.wikipedia.org/wiki/Snake_(video_game_genre)">a snake game</a>.</p>
<p>I tried to create a <a href="https://www.foddy.net/legacy/Athletics.html">QWOP-like</a> game for paddling a kayak.
<a href="https://quanttype.net/p/yearnote-2023/#outdoors-life">Back in 2023</a>, I did a nine-day kayaking trip on Lake Saimaa.
It was an emotional rollercoaster, type 2 fun, and I wanted to make a game to commemorate it.</p>
<p>Unfortunately I didn’t get the LLM to understand how kayaks work quickly enough, and I couldn’t get the controls to click.
The game was certainly frustrating but not especially fun.
Type 3 fun maybe?</p>
<figure>
<picture>
<img src="https://quanttype.net/p/software-for-myself/kayakqwop.png"
/>
</picture>
</figure>
<h2 id="implementing-research">Implementing research</h2>
<p>A while ago I heard about a data structure called <a href="https://ieeexplore.ieee.org/document/10597904">Bw<sup>e</sup> tree</a>.
It’s an evolution of B+ tree that is optimized for the current storage solutions.
You could use it to implement a database index, for example.</p>
<p>The paper was published by Alibaba and they’re using Bw<sup>e</sup> trees in production in some of their database services,
but they haven’t open-sourced their implementation.</p>
<p>I thought that why don’t I simply prompt an implementation into existence.
A couple of days with Claude Code and Opus 4.5 (this was in January) and I got some 10k lines of Rust.
It even has <a href="https://github.com/brianfrankcooper/YCSB">YCSB</a>-based benchmarks where it beats RocksDB, just like the paper said it would!</p>
<p>However…
I don’t know how to verify the implementation.
I have skimmed the paper, but I don’t have in depth understanding of it and 10k lines is a lot of code.
For example, the structural modification operations like page split look subtle and they’re crucial for correct concurrent operation.</p>
<p>According to the paper, Alibaba’s C++ implementation is 33k lines which makes the 10k number suspiciously low.</p>
<p>I’m unlikely to publish the code as I don’t know what’s in there.
If you need it, spend two days with Claude, or implement it by hand.
You’ll do better job.</p>
<h2 id="speeding-up-compression-algorithms">Speeding up compression algorithms</h2>
<p><a href="https://github.com/davebcn87/pi-autoresearch">pi-autoresearch</a> is an <em>autoresearch</em> plugin for the <a href="https://shittycodingagent.ai/">Pi agent</a>.
It prompts the agent to autonomously experiment to improve some numeric metric about the codebase.</p>
<p>That seems cool, so I tried it on <a href="https://github.com/miikka/floatbungler">floatbungler</a>.
Last year, I was studying lightweight float compression algorithms like <a href="https://quanttype.net/p/compressing-with-gorilla/">Gorilla</a>.
I implemented a few of them (by hand! I wanted to understand them!) and put them into a Python library.</p>
<p>I didn’t consider the performance at all when I wrote them, so I figured out I could unleash pi-autoresearch on the codebase and it would find ways to speed them up.
It sort of worked.
You can see <a href="https://github.com/miikka/floatbungler/pull/4">an example of what it did for Chimp128</a>.
It turned the code into a mess but the runtime benchmark did improve almost 3x.</p>
<p>There’s no need to make floatbungler faster, so I’m not going to merge the changes.
Better keep it simple and understandable in case I want to look at it again.</p>
<p>Playing around with pi-autoresearch revealed some flaws in the implementations and the test suite, so I did fix those.
The coding agents seem to be terrible at debugging the library, possibly because I haven’t built any debugging tools.</p>
<p>I did not notice attempts to cheat.
The agent did try some changes that broke the algorithm, but after the tests failed, it rolled them back.</p>
<h2 id="in-conclusion">In conclusion</h2>
<p>Crafting great products remains hard, but there’s a lot of fun in making software that’s good enough for exactly one person: yourself.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I’ve worked with software engineers who seem to have an urge to rewrite all the code they work on into their own style. Maybe I’m becoming one of them. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Goblin Mode, or spinning up VMs for feral agents
https://quanttype.net/p/goblin-mode/
Thu, 05 Mar 2026 00:00:00 +0000https://quanttype.net/p/goblin-mode/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/winter4_hu_12b4130a10dda03a.webp" type="image/webp" />
<img src="https://quanttype.net/images/winter4.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
<p>I’ve been working on a new tool for spinning up development environments.
It’s called <em>Goblin Mode</em>.
It spins up a new virtual machine (VM) on Hetzner and configures everything to be ready so that you can just SSH in and that start developing.</p>
<p>It is something that I’ve wanted to have for a while now, but the arrival of the coding agents got me to implement it.</p>
<p>The agents like Claude Code and Codex are excellent.
I’d like to run them in the YOLO mode / <code>--dangerously-skip-permissions</code> where they do not ask you for a permission to run commands, write files, search web, and so on.</p>
<p>However, my laptop has too much ambient authority for that. I’m logged in to all kinds of services and I’ve got a lot of important data there.
I don’t want an agent to mess with it.
A potential – but by no means the only – solution is to use a separate VM with just enough authority to get the job done.
The agents can then <a href="https://en.wikipedia.org/wiki/Goblin_mode">go goblin mode</a> there.</p>
<p>I created an alpha version of the tool and I’ve been using for a while.
It’s too specific to me and my workflows to be published right now, but it’s still interesting enough to talk about.</p>
<h2 id="how-does-it-work">How does it work?</h2>
<p>I had two goals:</p>
<ol>
<li>The environment should not need any manual setup. Running <code>gob up</code> should be all you need to receive an environment that works.</li>
<li>It should be cheap to run. By “cheap” I mean something like a few euros per month for my typical use.
For commercial development, much higher price would be acceptable, but I want to use this for my hobby projects, too.</li>
</ol>
<p>I ended up with a command-line tool implement in Rust.
When you run <code>gob up</code>, it does a few things:</p>
<p><strong>It spins up a project-specific VM on Hetzner.</strong>
Hetzner’s <a href="https://www.hetzner.com/cloud/">CX43 instances</a> are available for less than two cents per hour and they’re beefy enough for compiling Rust projects.
This is cheap enough for me to not to have to think about it unless I leave the box running all the time.</p>
<p><strong>It connects the box to Tailscale.</strong> Tailscale is a VPN solution that is really nice to use. Thanks to its <a href="https://tailscale.com/docs/features/magicdns">MagicDNS</a>, the box gets an easy-to-remember hostname that I can SSH to. For the projects that launch network servers, Goblin Mode uses <a href="https://tailscale.com/docs/features/tailscale-serve">Tailscale Serve</a> to expose the service over HTTPS. This great for developing web applications.</p>
<p><strong>It install the toolchain.</strong> Goblin Mode detects whether the project is a Python project or Rust project (my two main programming languages) and installs the appropriate tools. Some tools like git get always installed, and you can add extra packages via a config file.</p>
<p>This is implemented with <a href="https://cloud-init.io/">cloud-init</a>.
It’s a standard YAML config file for newly provisioned servers.
Many cloud providers, Hetzner included, support it.
Goblin Mode generates a list of packages to install and commands to run and adds them to cloud-init when launching the VM.</p>
<p>What is a bit annoying about this is that there are so many ways to install packages.
I run Debian and you can get many but not all packages via apt.
Some packages, like Claude Code, are best installed by <code>curl | bash</code> and for others I resort to <a href="https://github.com/cargo-bins/cargo-binstall">cargo-binstall</a>.
It would be nice to have a single solution - is Nix the answer?</p>
<p><strong>It sets up dotfiles.</strong>
Can you imagine doing anything without your custom zsh config?
Me neither.</p>
<p>In practice this is implemented by cloning your dotfiles git repository on the VM and running <a href="https://github.com/miikka/dotfiles/blob/master/install.sh">the included installation script</a> in it.</p>
<p><strong>It sets up the git repo.</strong> You’re expected to run <code>gob up</code> in the git repository for the project you’re developing.
Goblin Mode sets up a new repo on the VM, pushes the contents of the local repo there and copies the configuration for remotes over.</p>
<p>This has been enough for my projects right now.
I’ve added a few convenience commands, too:</p>
<ul>
<li><code>gob mosh</code> to <a href="https://mosh.org/">mosh</a> to the VM - in practice I always use mosh instead of SSH</li>
<li><code>gob zed</code> to open the project on the VM in Zed via its <a href="https://zed.dev/docs/remote-development">remote development support</a>.</li>
</ul>
<h2 id="problems">Problems</h2>
<p><strong>Spinning up VMs is slow.</strong> <code>gob up</code> takes something like two minutes.
It’s a bit annoying - you think “alright, I’m going to work a bit on project X” and then you have to wait for a few minutes while the server boots up.</p>
<p>I’m not sure if there’s any great solution to this that does not involve running more capacity than you need.</p>
<p>Also, while Hetzner is great, you aren’t guaranteed to get a cheap cloud VM when you need it. I’ve seen a bunch of these:</p>
<pre tabindex="0"><code>Error: Failed to create server (412): {
"error": {
"code": "resource_unavailable",
"message": "error during placement",
"details": {}
}
}
</code></pre><p><strong>Logging in to Claude Code is annoying.</strong> If you want to use Claude Code, you’ll have to go through the browser-based flow to log in. I’m not sure if this could be automated. Likely yes if you use the API; not sure if you use a Claude subscription.</p>
<p><strong>Restricting GitHub permissions is annoying.</strong>
GitHub does not make it too easy to grant just the right amount of permissions to the development VM.
Ideally the VM would be able to check out the project, work with the issues and create PRs, but not merge to main.
With fine-grained personal account tokens (PATs) and bot accounts this should be possible,
assuming you have a paid-for GitHub plan, but you can’t create PATs via GitHub’s API.</p>
<p>For now my solution has been to use a self-hosted <a href="https://forgejo.org/">Forgejo</a> instead of GitHub.
It’s not ideal, but it has enabled me to experiment without fear.</p>
<p><strong>Product management is still needed.</strong>
I had a bright idea that I’ll just dump my improvement ideas into Forgejo issues and let the agents work on them.
I created a script that loops through the issues and prompts an agent to solve each one and to create a PR.</p>
<p>A few hours of later I had a dozen of PRs and 5k lines of Rust to review.
The PRs were good but I quickly realized that my ideas were half-assed.
They were for nice to haves that could be great one day but that I did not yet need.
I didn’t want to maintain the code, so I left most of the PRs unmerged.</p>
<p>So, yeah, you still need to have a proper vision.</p>
<h2 id="successes">Successes</h2>
<p>I’ve enjoyed the tool more than I expected.
It’s nice when you can spin up a new environment with one command and everything actually works (modulo Claude Code auth).
It has also had a couple of benefits that I didn’t expect:</p>
<ul>
<li>If you want to demo a web service to your friends, you can expose it to Internet with Tailscale Funnel.
Start the service in tmux, leave the development VM running, close your laptop, and your friends can still access it.
It’s not a great production setup, but it’s excellent for trying things out.</li>
<li>If you need more capacity, provision a bigger VM.
My laptop has paltry 16 GB of RAM and I needed more for some data crunching, so I launched a big VM and got the job done.</li>
</ul>
<h2 id="related-work">Related work</h2>
<p>I know of at least four projects with similar goals:</p>
<ul>
<li><a href="https://containers.dev/">devcontainers</a> use Docker containers to create an ephemeral dev environment locally.</li>
<li><a href="https://github.com/superfishlu/gmab">gmab</a> (Give Me A Box) is a CLI tool that spins us ephemeral cloud boxes</li>
<li><a href="https://exe.dev/">exe.dev</a> is a neat commercial service that gives you virtual machines for development.
My project is sort of “we have exe.dev at home”.</li>
<li><a href="https://fly.io/blog/design-and-implementation/">Sprites by Fly.io</a> is another commercial take on the same idea.</li>
</ul>
Let's automate our jobs
https://quanttype.net/p/lets-automate-our-jobs/
Wed, 25 Feb 2026 00:00:00 +0000https://quanttype.net/p/lets-automate-our-jobs/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/winter3_hu_44e4854905556210.webp" type="image/webp" />
<img src="https://quanttype.net/images/winter3.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>Thanks to the coding agents like Claude Code, <a href="https://quanttype.net/posts/2026-02-12-programming-is-over.html">programming is now over</a>.
It’s more efficient to prompt an AI model to write code than it is to write it by hand.
However, programming is just one of the many tasks that a software engineer has to take care of, albeit a central one.
What about the other tasks? Can they be handled by an AI agent?</p>
<h2 id="the-software-delivery-loop">The software delivery loop</h2>
<p>When you work in a company that produces software, the software delivery works something like this:</p>
<ol>
<li>You start with some business problem to solve. You refine that into technical requirements for building the software that can provide the solution.</li>
<li>You build some code that matches the requirements and you test it.</li>
<li>You deliver the software to your users, often by deploying to production or by pushing an update to an app store.</li>
<li>You gather data on user behavior, customer feedback, business results and technical issues like bugs, production incidents, and performance problems.
This gives you some new business requirements. You go back to step 1.</li>
</ol>
<p>Claude Code can do a lot for step 2.
It cannot yet take care of it completely – right now, there’s a lot of writing prompts and reviewing results involved.
As the models improve, I expect them to be able handle more and more complex coding tasks.
Let’s look at what else can be automated.</p>
<p><strong>Getting the agents to complete the tasks autonomously.</strong> You shouldn’t be prompting <code>claude</code> manually. It should look at your GitHub issues or Jira backlog and start the work independently. Maybe you will review the PR it create, once you’ve had your agents review it first of course.</p>
<p>You already can do something like this by <a href="https://github.blog/ai-and-ml/github-copilot/assigning-and-completing-issues-with-coding-agent-in-github-copilot/">assigning tasks to GitHub Copilot</a> or by <a href="https://code.claude.com/docs/en/github-actions">mentioning @claude on GitHub</a>. But why isn’t Copilot or Claude deciding by itself what to work on?</p>
<p><a href="https://openclaw.ai/">OpenClaw</a> is an attempt to make the agents decide by themselves. It has already led to <em>interesting</em> results– like <a href="https://theshamblog.com/an-ai-agent-published-a-hit-piece-on-me/">an agent publishing a hit piece on a human open-source author</a>.</p>
<p><strong>Structuring big programs, architecture, and projects.</strong>
The agents are not that great yet at designing big programs.
Right now, fairly fine-grained tasks work the best. Maybe better models can solve this – or maybe having an hierarchy of models where your Software Architect model does the high-level design and creates tasks for the Coder models?</p>
<p>Steve Yegge’s <a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04">Gas Town</a> was a briefly-infamous attempt at building a hierarchy of agents that can complete tasks at autonomously. It was silly, but I think Yegge was on the right track and we’re going to see other takes on the same ideas. Claude Code already comes with <a href="https://code.claude.com/docs/en/sub-agents">subagents</a>.</p>
<p><strong>Running agents safely.</strong> If you’re running <code>claude</code> on your laptop, you’re giving it a lot of ambient authority. Are you logged into your company’s AWS production account? Great, so is Claude.</p>
<p>On one hand, this enables the agents do things and get the job done. On the other hand, the agents make mistakes all the time - just yesterday <a href="https://xcancel.com/summeryue0/status/2025774069124399363">someone used OpenClaw to accidentally delete their inbox</a>.</p>
<p>There are all kinds of attempts to build a sandbox (including the one <a href="https://code.claude.com/docs/en/sandboxing">in Claude Code itself</a>), but the best practices have yet to emerge.</p>
<p><strong>Setting up verification.</strong> The coding agents work the best when they can programmatically verify that what they produced is what you want. They’re good at fixing compiler errors and test failures. A big question for software engineers in near future is how to best provide the agents with these guardrails. How to go from the business requirements to <em>verifiable</em> technical requirements?</p>
<p>And, given how the agents are lazy in interesting ways, how to check that they’re not cheating with the verification?</p>
<p><strong>Operations and monitoring.</strong> There’s a whole bunch of work in keeping the lights on for software that runs in production. There are alerts, performance degradations, and runtime exceptions. Traditionally a software engineer or a site reliability engineer triages the issues and devises solutions. Could an agent do the triage instead? Can they debug the issues or connect them back in to the software delivery loop?</p>
<p>For example, if you have a data pipeline and something changes in the input data and now your ETL Python script complains about <code>NoneType</code>s, can you make an agent fix it without being involved yourself?</p>
<p><strong>Refining the business requirements into technical requirements.</strong> If you start from scratch, the coding agents are actually not too bad at this. They can do a lot with a simple prompt.</p>
<p>However, if you work in a big organization, there’s a lot of context that the agents are missing. They don’t know how to fit the new software into your architecture and tech roadmap. How are you going to give this information?</p>
<p><strong>Closing the loop for user and customer feedback.</strong> Your users and your customers have things to say about your software. You probably also have metrics that show what they’re doing. You may want to drive those metrics. How could agents do it?</p>
<hr>
<p>I’ve focused on things that could be attempted with the current AI models. Even if the models were frozen today, they’re so good that we could probably automate a lot more of software engineering work than we have already done.</p>
<p><a href="https://metr.org/blog/2025-03-19-measuring-ai-ability-to-complete-long-tasks/">The models are getting better</a> all the time, though.
For now, it’s an interesting opportunity to invent new ways of working. What it means for software engineering in the long run, that I don’t know.</p>
Programming is over
https://quanttype.net/p/programming-is-over/
Thu, 12 Feb 2026 00:00:00 +0000https://quanttype.net/p/programming-is-over/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/winter_hu_97b7c00299f76f2c.webp" type="image/webp" />
<img src="https://quanttype.net/images/winter.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
<p>Coding agents are so good now that they’re going to change the work of software engineers permanently. Allow me to have some takes<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<h2 id="programming-is-over">Programming is over</h2>
<p>Claude Code with Opus 4.5 has left a strong impression on me. It can code now. Not just a bit – it can create big chunks of decent enough code. The quality of the output is as good as that of many human developers but it works much faster. It can figure out complicated code bases, fix bugs, and add features.</p>
<p>This leads to my first hot take: <em>programming is over</em>.</p>
<p>By programming I mean writing and editing code by hand.
More and more of code will be written by
models. Writing code yourself will be relegated by and large to hobbyists. It will take a while for the change to take effect across the industry, but let’s say that in 2030 it’s going to be rare for most software engineers to write code by hand. Undoubtedly there will remain some holdouts of programming-by-hand far in the future.</p>
<p>This is subject to a bunch of assumptions:</p>
<ul>
<li>The models continue to improve. I don’t know how long the rapid improvement will continue, but I assume it has not yet tapered out.</li>
<li>The coding agents remain available for moderate prices. Maybe the financial shenanigans will crash the AI industry or maybe the leading companies will find a way to lock you in and jack up the prices.</li>
<li>The society stays stable. We live in tumultuous times and the AI tools themselves are part of the tumult. You know that thing where you use an AI model to run a company? A friend calls it strapping a paperclip maximizer to a paperclip maximizer.</li>
</ul>
<p>In any case, software engineering is not <em>yet</em> over. If anything, I expect that more software than ever gets produced. There remains room for people who are experts in it even if if the need for programming is gone.</p>
<h2 id="code-review-is-over">Code review is over</h2>
<p>The coding agents can produce a lot of code quickly. How do you know if the code does what it is supposed to do? You could review it. Programmers famously love reviewing – you never hear complaints about how slow the process is or about rubberstamped “Looks Good To Me” reviews.</p>
<p>Wait, no, that’s exactly what you hear about all the time. This leads to my next hot take: <em>code review is over</em>.</p>
<p>We’re not going to be reviewing all that code generated by the agents, whether the agents are operated by ourselves or our teammates. We might do some spot checks, but we will be relying on other ways of making sure it’s correct.</p>
<p>What are those ways going to be? <a href="https://factory.strongdm.ai/principles">People are trying to figure it out</a>. Testing the code is going to be a big deal, no doubt about that.</p>
<p>People who know me know that I’ve advocated for code review for a long time. It’s because I see it as a point of collaboration and forcing function for understandable code. If programming is theory-building, so is review. However, when it’s the agent working on the code, not you, the value of that work becomes ambiguous.</p>
<h2 id="what-about-theory-building">What about theory building?</h2>
<p>Peter Naur argued in <a href="https://gwern.net/doc/cs/algorithm/1985-naur.pdf"><em>Programming as Theory Building</em></a> (1985) that programming is not just about producing program text. Instead, it’s about the programmer developing insights – a theory -about how “certain affairs of the world will be handled by, or supported by, a computer program”. This enables them to explain why each part of the program is the way it is and, importantly, to modify the program.</p>
<p>With coding agents, we cede the production of program text to the agent. But what happens to the theory-building? More specifically:</p>
<ul>
<li>Do we humans still need to theory-build? Is it enough for us to have a high-level theory while the agent takes care of the low-level details? Can you debug a program without having a theory of it?</li>
<li>Does the agent have a theory of the program? How does it form it and where does it reside? Does the agent reconstruct it every time you start a new session?</li>
</ul>
<p>I don’t have clear answers.</p>
<p>A lot of debugging is like code archeology where you dig through layers of unfamiliar code.
In the short term, it will become even more so: now your own code is unfamiliar and you will have develop understanding on the spot.
In the long term as the agents catch up, debugging might be over, too.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The takes are hot, fresh off the pan. Better have them now, they might turn stale quickly. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Yearnote 2025
https://quanttype.net/p/yearnote-2025/
Wed, 07 Jan 2026 00:00:00 +0000https://quanttype.net/p/yearnote-2025/<p>Welcome to 2026.
It sure has been a year already in terms of world events.
Nevertheless, I’m going to talk a bit about how my 2025 was, <a href="https://quanttype.net/posts/2025-01-08-yearnote-2024.html">the same procedure as every year</a>.</p>
<p><em>On the photos: there’s one photo for each month, in chronological order.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-01_hu_584bf4f7c727963.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-01.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-02_hu_8ea285ec20beef6e.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-02.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="professional-life">Professional life</h2>
<p>I make my living by working as a software engineer.
It’s a job that I really enjoy: building software is a lot of fun.</p>
<p>Early in 2025, <a href="https://quanttype.net/posts/2025-01-31-new-winds.html">I left my job</a> and ventured out as an independent consultant.
Most of my year was spent with a big corporate client where I worked on modernizing the tools and the infrastructure for orchestrating their data processing jobs – classic data engineering work.
It was a nice project and I really enjoyed working with the team.
All in all, I’d consider my first year a success.</p>
<p>That said, it’s not like I have figured this business out.
I found the big gig by bumping into a friend at a party.
That was lucky, but now I need to do it again.</p>
<p>I’ve been struggling a bit to explain what it is that I want to do and what it is that I excel at.
In lieu of a focused pitch, here are a few things I do:</p>
<ul>
<li>Got a Python backend that is a bit slow? Would you like to make it go faster? Maybe <a href="https://miikka.me/talks/python-and-rust/">some Rust would help</a>? Hit me up.</li>
<li>Are you using Amazon S3 or another object storage in anger? Need someone to <a href="https://www.hytradboi.com/2025/46867aad-ddc3-4e7a-a530-941e1c363bba-why-s3s-conditional-writes-made-people-excited">dig deep into the details</a>? Hit me up.</li>
</ul>
<p>If you get me on your team, I’ll also fix your development environment<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, make your <a href="https://quanttype.net/posts/2024-12-08-weeknote-17-caching-docker-builds.html">GitHub Actions pipeline’s cache work</a>, be endlessly patient with AWS IAM problems, and foster an enviroment <a href="https://mastodon.social/@Miikka/115750866123214116">where it’s okay to not know things</a>.</p>
<p>Now I’m again open to new engagements.
If you would like to work with me, <a href="mailto:[email protected]">send me an e-mail</a> or <a href="https://www.linkedin.com/in/miikkakoskinen/">connect on LinkedIn</a> and let’s talk.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-03_hu_4cfae3586c65d2a6.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-03.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-04_hu_cbe7d6e6e3ffd6d2.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-04.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="software-engineering-community">Software engineering community</h2>
<p>My main way of engaging with the software engineering community was public speaking.</p>
<ul>
<li>I gave a lightning talk at HYTRADBOI about <a href="https://www.hytradboi.com/2025/46867aad-ddc3-4e7a-a530-941e1c363bba-why-s3s-conditional-writes-made-people-excited">Why S3’s conditional writes made people excited</a> (see also the <a href="https://quanttype.net/posts/2025-02-25-leader-election-with-s3-and-if-match.html">the companion blog post</a>).</li>
<li>In May, I gave a talk about <a href="https://quanttype.net/posts/2025-06-16-compressing-with-gorilla.html">compressing floating point data with Gorilla</a> at Helsinki Python Meetup.</li>
<li>In September at PyCon UK and again in October at PyCon Finland, I gave a talk about <a href="https://miikka.me/talks/python-and-rust/">using Python and Rust together</a>. I also wrote a post on <a href="https://quanttype.net/posts/2025-09-12-uv-and-maturin.html">setting up uv and Maturin</a> so that they work together.</li>
</ul>
<p>It was fun to give the talks and I’m hoping to continue it this year.
I have at least one idea for a talk about data storage.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-05_hu_4d9b0247911c3022.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-05.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-06_hu_9ac46d082b83affd.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-06.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="other-life-events">Other life events</h2>
<p>I bought a home. I wasn’t planning for it, but the landlord decided to sell the apartment I was living in and after taking a look at the housing market, I ended up buying it. Now I get to renovate it. It would be amazing to finally live in an apartment with a dishwasher.
Wish me luck.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-09_hu_1e855bdd3550b474.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-09.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-10_hu_14ef4eb06c64c6a9.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-10.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="outdoors-life">Outdoors life</h2>
<p>It was a weird year for me. I had no big adventures (apart from sailing from Scotland to Norway), which feels a bit disappointing. On the other hand, I climbed more than ever, skated more than ever, and ran more than ever.</p>
<p>It was a year of courses. I took <a href="https://buttondown.com/smallrapids/archive/the-skating-season-is-over/">a tour skating course</a>, two kayaking courses, two climbing courses (introductions to lead climbing and climbing outdoors), and two first aid courses. I also acted as an assistant teacher on an introductory kayaking course.</p>
<p>I tend to take courses too late, so that I already know the stuff they’re going to teach.
Still, the social element of learning together is enjoyable.</p>
<p>I did publish a couple of posts on my outdoors newsletter <a href="https://smallrapids.net/">Small Rapids</a> - see the <a href="https://buttondown.com/smallrapids/archive/">archive</a>. The most popular one was the one about ditching Spotify called <a href="https://buttondown.com/smallrapids/archive/you-can-listen-to-mp3s/"><em>You can still listen to .mp3s</em></a>.</p>
<p>In 2026, I’m going to do a big hike again, that’s for sure.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-07_hu_1e84435c9479954e.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-07.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-08_hu_59b566d56f77774e.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-08.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="best-of-2025">Best of 2025</h2>
<ul>
<li><strong>Best new album:</strong> <a href="https://humurecords.bandcamp.com/album/neko-a-sekai"><em>Neko-a-Sekai</em></a> by Tinyhawk & Bizzarro. They play groovy instrumental rock. It’s both a lot of fun and it leaves me in awe of their skill.</li>
<li><strong>Best book read:</strong> <em>Matara</em> by Matias Riikonen. It’s about a group of boys spending the summer playing society that just so happens to resemble Ancient Rome. I enjoyed Riikonen’s rich language and how the book takes play seriously. (The book has not yet been translated to English.)</li>
<li><strong>New favorite writer:</strong> Anne Carson. Have you read <em>Autobiography of Red</em>? There’s surprisingly lot to it considering how little it is.</li>
</ul>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-11_hu_9184f0b665717296.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-11.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2025-12_hu_525e924309717a07.webp" type="image/webp" />
<img src="https://quanttype.net/images/2025-12.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p><em>In each yearnote, I express (lack of) surprise at the current cabinet of the goverment of Finland.</em></p>
<p>Like I predicted, prime minister Petteri Orpo’s cabinet held together through the whole year. It wasn’t because lack of scandals! However, Orpo has his eyes on the ball and will get through every crisis to reach his party’s policy goals.</p>
<p>Nevertheless I believe that the cabinet will fall apart before the parliamentary election of 2027.
The cabinet has already got massive policy changes done and there’s always a chance to score a few points in the eyes of voters by breaking up.
As the election approaches, I bet both True Finns and Swedish People’s Party will get anxious about their popularity.
Thus it’s more likely than not that the cabinet will break up before the year is over.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Fixing the development environment used to mean things like making sure that the tests can be run locally and that the IDE features work. In 2026, that probably also means ensuring that the coding agents can be run reliably and <em>safely</em>. New challenges! <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
PyCon Finland 2025
https://quanttype.net/p/pycon-finland-2025/
Sun, 19 Oct 2025 00:00:00 +0000https://quanttype.net/p/pycon-finland-2025/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ylisto_hu_2b5f69f038db2157.webp" type="image/webp" />
<img src="https://quanttype.net/images/ylisto.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>After a nine-year break, PyCon Finland was back this Friday as a part of PloneConf 2025.</p>
<p>I took a morning train to Jyväskylä and gave my talk about <a href="https://miikka.me/talks/python-and-rust/">extending Python with Rust</a>.
It went fine.
I went through the slides so fast that I must have skipped over something, but thankfully there were a lot of great questions from the audience so we made good use of the extra time.
A talk video will be uploaded eventually.</p>
<p>Like at <a href="https://quanttype.net/posts/2025-09-29-pycon-uk-2025.html">PyCon UK</a>, I bumped into a few people who already are extending Python with Rust.
In the talk, I claim that it’s a pattern, a trend.
The evidence for the trend keeps accumulating.</p>
<p>I didn’t catch many other talks, but <a href="https://2025.ploneconf.org/schedule/keynotes/muuttolintujen-kevat-automatic-bird-sound-classifier">the keynote by Patrik Lauha</a> about <em>Muuttolintujen kevät</em> was a delight.
It’s a mobile app that can automatically recognize bird songs from recordings, tailored for the bird species in Finland.
If you hear a bird that you can’t recognize, you can record its song with your phone and the app will tell you the species.</p>
<p>Patrik is the scientist who has developed the machine learning model behind the app.
His talk covered how the app works and how they’re using the data they’ve gathered through it.
Through the citizen science style data collection, they’ve been able to make existing bird distribution models more accurate.
Cool stuff!</p>
<p>Saturday was the sprint day.
Most of the people were working on Plone and Plone-related projects,
but Hugo van Kemenade was running a CPython sprint.
I sat down with him and a few other people to work on CPython issues.
That was nice, and a first for me. I haven’t attempted to contribute to CPython before.</p>
<p>The combination of frosty weather and autumn leaves made Jyväskylä look beautiful, especially around the Ylistö campus of University of Jyväskylä.
I posted <a href="https://mastodon.social/@Miikka/115393733150399609">a few photos</a> on Mastodon.</p>
<p>It was great to see PyCon Finland happening again and I’m glad I got a chance to give a talk there.
Thanks to all the organizers, sponsors, and attendees for making it happen and making it fun!</p>
<p>Check out <a href="https://hamatti.org/posts/pycon-finland-2025-recap/">Juha-Matti Santala’s conference report</a>, too.</p>
PyCon UK 2025
https://quanttype.net/p/pycon-uk-2025/
Mon, 29 Sep 2025 00:00:00 +0000https://quanttype.net/p/pycon-uk-2025/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/contact_hu_e08e75513d6d100d.webp" type="image/webp" />
<img src="https://quanttype.net/images/contact.jpg" width="600" height="480" fetchpriority="high" />
</picture>
</figure>
<p>Welcoming. That’s the word that comes to my mind when I think about <a href="https://2025.pyconuk.org/">PyCon UK 2025</a>.</p>
<p>PyCon UK is the main Python conference in the UK.
This year’s edition took place a week ago - mid-September - in Manchester at Contact Theatre.
I attended the conference in person and gave a talk, too.</p>
<h2 id="my-way-to-manchester">My way to Manchester</h2>
<p>In May, I was thinking about extending Python with Rust.
I had done it at work and in a hobby project and it had turned out to be far easier and more convenient than I thought.
People should know about that!</p>
<p>When I saw someone advertising the PyCon UK CFP, I figured out that I might as well pitch a talk about it.
I used to think that conferences and giving talks was not for me.
However, <a href="https://quanttype.net/posts/2024-09-25-weeknote-6-heart-of-clojure.html">attending Heart of Clojure</a> made me <a href="https://mastodon.social/@Miikka/114668523908288527">change my mind</a>.</p>
<p>To my surprise, the talk proposal was accepted.
Another surprise was that it attracted full audience.</p>
<p>The title of the talk is <em>Python and Rust, a perfect pairing</em> and it is about extending Python with Rust using PyO3, Maturin, and rustimport.
The <a href="https://www.youtube.com/watch?v=idgq9ZhQWCo">talk video</a> is already on YouTube and <a href="https://miikka.me/talks/python-and-rust/">the slides are here</a>. Check it out!</p>
<p>If you’d like to see me present it live, you still have a chance.
I’m going to give the talk again at <a href="https://2025.ploneconf.org/pyconfi-2025">PyCon Finland 2025</a> in Jyväskylä on Oct 17 - slightly improved based on feedback.</p>
<h2 id="the-other-talks">The other talks</h2>
<p>You can fit a lot of talks in three days.
What most piqued my interest were the talks about Python performance.
Sasha Romijn talked about <a href="https://www.youtube.com/watch?v=TOpsv1tpf1c">Python performance mistakes</a>.
Kolen Cheung compared <a href="https://www.youtube.com/watch?v=4gVWeLfMdgc">Numba and JAX</a>.
Peichao Qin talked about <a href="https://www.youtube.com/watch?v=2znWvOXwQ4Y">extending Python with C++ and Pybind11</a>.
My talk contributed to this theme as well.</p>
<p>Out of the three keynotes, my favorite was the one by Sheena, titled <a href="https://www.youtube.com/watch?v=b0GqRDfumR8"><em>Playing the long game</em></a>.
It was a balanced take on the LLM tools and how they might be affecting software development careers, especially the people early in their careers.</p>
<p>It’s a different tack, but there was a rehearsed reading of <a href="https://www.youtube.com/watch?v=CtrsssksCNU">Emily Holyoake’s play Ada</a>. I enjoyed it a lot.</p>
<h2 id="the-hallway-track">The hallway track</h2>
<p>The hallway track is the most important track in every conference.</p>
<p>For me, it was a chance to meet a bunch of Internet friends in person for the first time.
That was great! I also met with a number of new people and talked with them about Python and Rust, and about climbing and life.
That was great, too.</p>
<p>So what made the conference welcoming?
It’s not just one thing, but the conference’s <a href="https://2025.pyconuk.org/inclusion/">inclusion efforts</a> seemed to pay off, <a href="https://mastodon.social/@hynek/115260756910812062">as noted by keynote speaker Hynek Schlawack</a>.</p>
<h2 id="manchester">Manchester</h2>
<p>I didn’t have a chance to see that much Manchester, but I did see Oxford Road a lot.
My hotel was on Oxford Road, as was the conference venue, as was the main bar we went to, as was Manchester Museum (<a href="https://mastodon.social/@Miikka/115241692168593161">they have frogs</a>),
as were the coffee places I had my morning coffee at.</p>
<p>After the conference, I did <a href="https://mastodon.social/@Miikka/115254145347936538">a walk in Peak District</a> near Kinder Scout. The day was stunning and the scenery beautiful. This time, I was lugging all my stuff with me, so I had to walk.
The next time, it would be great to run the route and explore further.
If there’s a PyCon UK in Manchester in 2026, I will do it.</p>
uv and maturin
https://quanttype.net/p/uv-and-maturin/
Fri, 12 Sep 2025 00:00:00 +0000https://quanttype.net/p/uv-and-maturin/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/duck_hu_48a11605d6c1c5fe.webp" type="image/webp" />
<img src="https://quanttype.net/images/duck.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p><a href="https://docs.astral.sh/uv/">uv</a> is a Python package and project management tool.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
<a href="https://www.maturin.rs/">Maturin</a> is a build backend for Python extension modules implemented in Rust.
How to best use them together?</p>
<h2 id="why-uv">Why uv?</h2>
<p>First of all, if you’ve already got Maturin, why do you need uv? There are a couple of reasons:</p>
<ul>
<li>uv takes care of the virtualenv management.</li>
<li>You can use uv to install development dependencies such as pytest.</li>
</ul>
<p>You could use another virtualenv manager such as <a href="https://tox.wiki/">tox</a>.
Myself, I wanted to use uv because I’m already familiar with it and it’s fast.</p>
<h2 id="setting-up-the-project">Setting up the project</h2>
<p>Start by creating a new project the usual way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">maturin new my_project
</span></span></code></pre></div><p>Among other things, this creates a standard <code>pyproject.toml</code> that works with uv.
If you run now <code>uv sync</code>, uv will build the project using Maturin’s build backend, create a virtualenv and install the package there.</p>
<p>However, there is a problem: when you edit your Rust source files, there’s no easy way to rebuild the package.
Running <code>uv sync</code> will not help because uv does not know that the Rust source files affect the package.</p>
<p>Running <code>maturin develop</code> does rebuild the package, but if you do anything with uv that triggers a sync - for example, using <code>uv run</code> - then it will re-install the cached old version of the package.</p>
<p>Below, I present three options for making it work.</p>
<h2 id="option-1-let-uv-handle-the-rebuild">Option 1: Let uv handle the rebuild</h2>
<p>You can tell uv that the package needs to rebuild whenever the Rust source changes.
To do that, you’ll need to set <a href="https://docs.astral.sh/uv/reference/settings/#cache-keys"><code>tool.uv.cache-keys</code></a> in <code>pyproject.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">uv</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">cache-keys</span> <span class="p">=</span> <span class="p">[{</span><span class="nx">file</span> <span class="p">=</span> <span class="s2">"pyproject.toml"</span><span class="p">},</span> <span class="p">{</span><span class="nx">file</span> <span class="p">=</span> <span class="s2">"Cargo.toml"</span><span class="p">},</span> <span class="p">{</span><span class="nx">file</span> <span class="p">=</span> <span class="s2">"**/*.rs"</span><span class="p">}]</span>
</span></span></code></pre></div><p>Note that even if you have a mixed Python/Rust package, the package does not need to rebuild when the Python files change.
<code>uv sync</code> by default installs the package as editable, so the Python interpreter uses the source files directly.</p>
<p>There’s a downside to this approach: <code>uv sync</code> builds the package in the release mode which yields faster code but slower build times
than the debug mode used by <code>maturin develop</code>.</p>
<h2 id="option-2-use-maturin-develop">Option 2: Use maturin develop</h2>
<p>The alternative is to not install the project package at all when running <code>uv sync</code>.
Edit <code>pyproject.toml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tool</span><span class="p">.</span><span class="nx">uv</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">package</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></div><p>Now if you run <code>uv sync</code>, it will install dependencies but not the project itself.
You need to install it manually with Maturin:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">maturin develop
</span></span></code></pre></div><p>You will have to re-run this command whenever you change the Rust files.</p>
<h2 id="option-3-use-maturin-import-hook">Option 3: Use maturin import hook</h2>
<p>If you want to use <code>maturin develop</code>, but you also want the code to rebuild automatically whenever it changes,
then the <a href="https://www.maturin.rs/import_hook.html">Maturin import hook</a> is the way to go.
It rebuilds the code if needed when you <code>import</code> it in Python.</p>
<p>Like in option 2, you need to set <code>tool.uv.package</code> to false.
You also need to set the minimum required Python version to 3.9 or higher - the default by <code>maturin new</code> is 3.8:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">project</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c"># ...</span>
</span></span><span class="line"><span class="cl"><span class="nx">requires-python</span> <span class="p">=</span> <span class="s2">">=3.9"</span>
</span></span></code></pre></div><p>Then, let’s install the import hook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uv add --dev maturin_import_hook
</span></span><span class="line"><span class="cl">uv run -m maturin_import_hook site install
</span></span><span class="line"><span class="cl">maturin develop
</span></span></code></pre></div><p>You need to install the project with <code>maturin develop</code> once, but after that the import hook will take care of the rebuilds.</p>
<p><strong>Gotcha:</strong> If you’re on macOS and you use Python installed with Mac Homebrew, <a href="https://github.com/PyO3/maturin-import-hook/discussions/26">this will not work</a> with Maturin 0.3.0.
A workaround is to use uv-managed Python: <code>uv sync --managed-python</code></p>
<h2 id="managing-python-dependencies">Managing Python dependencies</h2>
<p>You can manage Python dependencies with the usual <code>uv</code> commands.
For example, to add pytest as a development dependency:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">uv add --dev pytest
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>In my opinion, uv is the snappiest and the easiest-to-use package management tool for Python right now and it’s the one that I recommend to most people. I’ve previously recommended Poetry, but uv has surpassed it in features and it was always much faster. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Compressing floating point data with Gorilla
https://quanttype.net/p/compressing-with-gorilla/
Mon, 16 Jun 2025 00:00:00 +0000https://quanttype.net/p/compressing-with-gorilla/<p>Last week, I gave a talk titled <em>Compressing floating point data with Gorilla</em> at <a href="https://www.meetup.com/helpy-meetups/events/308064249/">Helsinki Python Meetup</a>.
There’s no recording, but here is a blog version of the slides.</p>
<p>Gorilla is a simple algorithm for compressing floats.
It’s over a decade old and it’s not the state of the art anymore but since it sparked the development of <a href="https://quanttype.net/posts/2025-05-07-fp-compression-family-tree.html">a whole family</a> of algorithms, it’s a great starting point for learning about this topic.</p>
<p>I had a lot of fun learning about this and I figured others would enjoy it as well.
You get to learn a bit about floats, time series data, and data compression.
I hope you like it!</p>
<hr>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-01.png"
alt="Title slide for presentation 'Compressing floating point data with Gorilla' by Miikka Koskinen, presented at Helsinki Python Meetup in June 2025, with Jacksnipe Consulting logo"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-02.png"
alt="Agenda slide showing four parts: Part 1 Introduction (Who am I, What is Gorilla, What is time series data), Part 2 Floats (How do floating point numbers work), Part 3 Gorilla (How does the Gorilla algorithm work), Part 4 Is it any good (Should you use Gorilla)"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-03.png"
alt="Section divider slide showing 'Part 1: Introduction' in large text"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-04.png"
alt="Speaker introduction slide: Miikka Koskinen, software engineer and independent consultant at Jacksnipe Consulting, previously worked at Oura, Metosin, ZenRobotics, and Futurice. Interested in big data problems. Uses Python, Rust, and AWS. Blog at quanttype.net"
/>
</picture>
</figure>
<p>Actually, don’t just read this blog, <a href="https://quanttype.net/subscribe/">go subscribe!</a></p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-05.png"
alt="Slide about Gorilla: A timeseries database developed by Facebook for monitoring services, described in a 2015 paper by Pelkonen et al. titled 'Gorilla: A Fast, Scalable, In-Memory Time Series Database'. Includes a simple, clever scheme for compressing floating point data"
/>
</picture>
</figure>
<p>The talk title mentions the Gorilla algorithm, but the actual Gorilla was a time series database developed by Facebook.
It was not ever made publicly available but they described it in a <a href="https://www.vldb.org/pvldb/vol8/p1816-teller.pdf">paper</a> published in 2015.</p>
<p>They were trying to monitor web backends, including metrics like CPU load and network latency, and they wanted to be able to query recent data quickly.
Because of this, they designed a new database that could hold the data in the memory.
It was pretty much an observability tool, although they didn’t call it that back then.</p>
<p>To make the data fit in the memory, they came up with a simple but efficient encoding for the floats.
This encoding is the algorithm that we’re talking about.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-06.png"
alt="Time series data explanation: When you collect measurements over time, you get time series data. Examples include network latency, software memory usage, heartbeat, room temperature, humidity, electricity usage, location, and stock price. Includes a data table and temperature graph visualization"
/>
</picture>
</figure>
<p>There are many general purpose compressing algorithms like <a href="https://en.wikipedia.org/wiki/Deflate">DEFLATE</a> and <a href="https://en.wikipedia.org/wiki/Zstd">Zstandard</a>.
These algorithms need to work on any data you can throw at them.
However, if you know something about the data you’re going to be compressing,
you may be able to design an algorithm that is faster or compresses better or uses less memory than the general purpose algorithms.</p>
<p>With Gorilla, we’re going to compress time series data, which is data where the data points are associated with a timestamp.
Do we know anything about time series data that we could use?</p>
<p>One thing is that it’s often produced by some sort of slow process.
For example, on the slides you can see some temperature data from my home.
As you can see, the numbers are roughly the shame.
The temperature changes slowly and within a limited range.
It’s not going to suddenly jump to plus or minus hundred degrees.</p>
<p>Here’s an insight we need: in time series data, the consecutive values typically resemble each other and stay within a narrow range.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-07.png"
alt="Section divider slide showing 'Part 2: Floats' in large text"
/>
</picture>
</figure>
<p>Before we can talk about the algorithm itself, we need know a bit about how floats work.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-08.png"
alt="What floats are used for: Floats solve two problems - representing non-integral numbers like 1.25, and representing very large or small numbers. Python uses IEEE-754 binary64 double precision floats. Shows Python code example of type(1.25) returning float class"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-09.png"
alt="Float decomposition explanation: Floats work by decomposing numbers as x = (-1)^sign * 2^exponent * 1.significand, where sign is 1 bit, exponent 11 bits, and significand 53 bits. Example shows 1.25 decomposition. Demonstrates floating point precision issues with 0.1 + 0.2 = 0.30000000000000004"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-10.png"
alt="Demo slide directing viewers to visit https://float.exposed/0x3ff0000000000000 to explore floating point representation"
/>
</picture>
</figure>
<p>Link: <a href="https://float.exposed/0x3ff0000000000000">https://float.exposed/0x3ff0000000000000</a></p>
<p>float.exposed is a web playground for seeing how computers represent the floats.
You can enter a float at the top and it shows you the bits and the decomposition.</p>
<p>You can click the bits to toggle them.</p>
<ul>
<li>See what happens when you toggle the sign bit (marked with red underline).</li>
<li>See what happens when you increase or decrease the exponent.</li>
<li>Toggle the bits in the significand (marked with blue underline) and see if you can do the binary math in your head to verify the result.</li>
</ul>
<p>Try entering <code>1.25</code>.
The slides said that the exponent would be 0 but float.exposed says it’s 1023.
That’s because the value is <em>biased</em>.
You have to subtract 1023 to get the actual exponent.
This is what allows us to store negative exponents in that fields - try entering 1 in the exponent and see what you get.</p>
<p>We don’t have to remember all of this to understand Gorilla.
However, it’s good to notice that the sign bit and the exponent come first.
This means that if we’ve got a bunch of numbers that a roughly the same, and they aren’t right around zero,
the left-hand side of their bit representations is going to be roughly the same as it’s only the significand that changes.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-11.png"
alt="Section divider slide showing 'Part 3: Gorilla' in large text"
/>
</picture>
</figure>
<p>Now we’re ready to talk about the algorithm!</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-12.png"
alt="The main insights in Gorilla algorithm: Consists of two tricks - XORing consecutive values and encoding the results carefully"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-13.png"
alt="XOR explanation: XOR is a bitwise operator that yields 1 when exactly one input is 1. Shows truth table and Python examples using the ^ operator with integers and binary representations"
/>
</picture>
</figure>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-15.png"
alt="XOR trick demonstration: Shows binary representations of 20.5 and 21.0, and their XOR result, illustrating how XOR zeros out bits that are the same between similar floating point numbers"
/>
</picture>
</figure>
<p>The second column shows the binary representation for the inputs.
I made up the syntax in the third row - you can’t actually xor two floats in Python - but I’m using it to mean the xor of the binary representations.</p>
<p>Xor sets the bits that differ to one and the bits that are the same to zero. See the highlighted bits - they are the only two that differ in the inputs.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-17.png"
alt="XOR reversibility demonstration: Shows that XOR is reversible by demonstrating that (20.5 XOR 21.0) XOR 21.0 equals the original 20.5, proving data can be recovered"
/>
</picture>
</figure>
<p>Xor is also reversible. If you xor 20.5 with 21.0 and then xor the result with 21.0 again, you’ll get 20.5 back.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-18.png"
alt="Terminology for XOR result: Diagram showing a 64-bit XOR result with labels for leading zeros, meaningful bits, and trailing zeros sections"
/>
</picture>
</figure>
<p>Now if we look at the xor result, we can see there’s a run of leading zeros.
Then there’s a handful of <em>meaningful bits</em>.
Now it’s just two ones but there could be some zeros in the middle.
And finally there’s a run of trailing zeros.</p>
<p>Here’s how we’re going to encode this: we’re going to store the number of zeros instead of the actual zero bits.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-19.png"
alt="Encoding scheme explanation: Store first value as-is, then for each subsequent value, store XOR with previous value using three encoding rules - '0' for all zeros, '10' plus meaningful bits if they fit within previous block, or '11' plus leading zero count, meaningful bit count, and meaningful bits"
/>
</picture>
</figure>
<p>We’re going to store the first value as-is and then for the remaining values, the value xorred with the previous value.
Since xor is reversible, we can revert this process when we’re decoding the data.</p>
<p>Then we encode the values as described in the slides.</p>
<p>In the second case (control bits <code>10</code>), the precise condition is that the number of leading zeros is the same or greater than in the previous value and the number of trailing zeros is the same.
If you have more leading zeros than in the previous value, you will have to include those zeros in the meaningful bits.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-20.png"
alt="Demo slide with localhost URL for interactive Gorilla compression demonstration with sample float values"
/>
</picture>
</figure>
<p>Link: <a href="https://miikka.github.io/python-gorilla/?floats=20.5%2021.0%2021.0%2021.2%2021.1%2020.9">https://miikka.github.io/python-gorilla/?floats=20.5%2021.0%2021.0%2021.2%2021.1%2020.9</a></p>
<p><em>Okay, sorry blog readers, this part would really benefit from a screencast with me gesturing at things.</em></p>
<p>This is a playground that you can use to see the algorithm in action.
Use it to step through the compression of the values <code>20.5 21.0 21.0 21.2 21.1 20.9</code>.</p>
<ul>
<li>The input is in the first column and the output is in the third column.</li>
<li>The second column shows the binary representation. The first value is as-is and the remaining values have been xorred with the previous value.</li>
<li>You can hover the highlighted blocks to see what they mean.</li>
</ul>
<p>Background color guide for the highlighted blocks:</p>
<ul>
<li><em>Green</em>: control bits</li>
<li><em>Orange</em>: the number of leading bits</li>
<li><em>Violet</em>: the number of meaningful bits</li>
<li><em>Navy</em>: the meaningful bits themselves</li>
</ul>
<p>In the last step you can see that the compressed data uses 41.17 bits/value. Uncompressed data would use 64 bits/value.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Is this good performance? We’ll return to this question in a moment.</p>
<p>Note how the changing bits are mostly in the middle or on the right-hand side.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-21.png"
alt="Code reference slide pointing to GitHub repository at https://github.com/miikka/python-gorilla"
/>
</picture>
</figure>
<p>Link: <a href="https://github.com/miikka/python-gorilla">https://github.com/miikka/python-gorilla</a>. See <a href="https://github.com/miikka/python-gorilla/blob/main/src/python_gorilla/__init__.py">the implementation</a> and <a href="https://github.com/miikka/python-gorilla/blob/main/tests/test_gorilla.py">the test suite</a>.</p>
<ul>
<li>The implementation, including encoding and decoding, is just a bit over 100 lines, so you can see it’s a fairly simple algorithm. The algorithm uses little memory since all the state it needs to keep is the previous value.</li>
<li>I used the <a href="https://bitstring.readthedocs.io/en/stable/">bitstring</a> library to do bitwise IO. It was okay, but it’s easy to do the bit twiddling with ints and do the bitwise IO yourself.</li>
<li>The test suite has just one test but it’s a pretty good one since it’s a property-based test powered by <a href="https://hypothesis.readthedocs.io/en/latest/">Hypothesis</a>.</li>
</ul>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-22.png"
alt="Section divider slide showing 'Part 4: Is it any good?' in large text"
/>
</picture>
</figure>
<p>How do you know if a compression algorithm is any good?
One way to do is to gather a set of data that you want to compress and run benchmark on it, comparing it to other algorithms.
I believe Facebook did this, but the Gorilla paper is a bit thin on the results.
Luckily there are some later papers that have more informative benchmarks.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-23.png"
alt="Benchmark results chart comparing compression algorithms by bits per value vs compression time, showing Gorilla, Chimp, FPC, Snappy, Zstd, LZ4, Brotli, and Xz performance. Source citation from Chimp paper by Liakos et al., 2022"
/>
</picture>
</figure>
<p><a href="https://www.vldb.org/pvldb/vol15/p3058-liakos.pdf">Chimp</a> is an algorithm directly inspired by Gorilla.
They ran benchmarks over a number of time series data sets and here’s one of the graphs they produced.</p>
<p>The horizontal axis is bits/value and the vertical axis is how long the compression takes.
In both cases, smaller is better.</p>
<p>We can compare Gorilla (blue circle) to Zstd (orange circle), which is a great general purpose compression algorithm.
Gorilla seems to produce about twice as many bits and to be four times faster.</p>
<p>Is this a good tradeoff? It could be - you have to decide.
In any case, Zstd was published in only 2016, so it did not exist in public when Gorilla was published.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-24.png"
alt="Second benchmark results chart showing compression ratio vs speed tradeoffs for various algorithms including ALP, Gorilla, Chimp, Zstd, and others. Shows compression ratios from 1.5x to 3.1x. Source citation from ALP paper by Afroozeh et al., 2024"
/>
</picture>
</figure>
<p>Here’s another benchmark from <a href="https://dl.acm.org/doi/pdf/10.1145/3626717">the ALP paper</a>.</p>
<p>The horizontal axis is decompression speed and the vertical axis is compression speed.
The compression rate is in the labels.
For all of them, the higher the better.
You can see that Zstd (black) seems to actually beat Gorilla (green) in decompression speed.</p>
<p>ALP, published in 2024, is the state of the art algorithm for compressing floats.
It’s significantly more complex but it also delivers results.
As you can see, it’s right there in the upper-right corner with excellent 3x compression ratio.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-25.png"
alt="Slide explaining that Gorilla can compress any data, not just valid floats, as it uses tricks that happen to work well on floating point data but might not be optimal for all data types"
/>
</picture>
</figure>
<p>The algorithm does not actually make use of the float structure directly, so you can compress any data with it. I have not tried it, but you could compresse your web pages or source tarballs with Gorilla if you wanted, as long as you pad the length to be a multiple of 8 bytes. I expect that the compression ratio is rather poor.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/compressing-with-gorilla/slide-26.png"
alt="Conclusion slide summarizing key points: time series data points are typically similar, XORing similar floats produces 1 bits in the middle, Gorilla was good historically but ALP is recommended now. Includes GitHub link and social media references"
/>
</picture>
</figure>
<p>Most of you probably won’t be implementing these Gorilla or Chimp or ALP yourself, but you might be already using some of them indirectly.
For example, the analytics database <a href="https://duckdb.org/">DuckDB</a> implements Chimp and ALP.</p>
<p>Thanks for reading!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The astute reader might notice that the string <code>20.5</code> uses only four bytes, or 32 bits, whereas storing it as a float uses 8 bytes, or 64 bits. Another idea for a compression algorithm right there! <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
FP compression family tree
https://quanttype.net/p/fp-compression-family-tree/
Wed, 07 May 2025 00:00:00 +0000https://quanttype.net/p/fp-compression-family-tree/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rollarit_hu_ab7b1acca41c379a.webp" type="image/webp" />
<img src="https://quanttype.net/images/rollarit.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<aside><em>Nota bene:</em> This is a notebook post. I'm jotting down
some notes, often based on an ongoing project I'm working on.
There will be less context and explanation than in my normal posts.
</aside>
<p>I’ve been looking into algorithms for compressing floating-point data in columnar databases.
Here’s a family tree for a few of them.</p>
<figure>
<picture>
<img src="https://quanttype.net/p/fp-compression-family-tree/algos.svg"
/>
</picture>
</figure>
<p><strong>Gorilla</strong>: The original algorithm based on xorring the consecutive values.
Described in 2015 by Facebook in the
<a href="https://www.vldb.org/pvldb/vol8/p1816-teller.pdf">paper</a> describing their
Gorilla timeseries database.</p>
<p><strong>Chimp</strong>: Improves upon Gorilla by using more complicated, more efficient
encoding for the xorred values. <a href="https://www.vldb.org/pvldb/vol15/p3058-liakos.pdf">Paper</a> from 2022.</p>
<p><strong>Chimp128</strong>: Instead of xorring the current value with the previous value,
look at the previous 128 values and xor the current value with the one that
produces the most trailing zeros. Published along with Chimp.</p>
<p><strong>Patas</strong>: The algorithms above produce a stream of bits which makes the IO inefficient.
Patas is a simplification of Chimp128 that produces a stream of bytes instead.
The compression ratio suffers, but it goes much faster.
<a href="https://github.com/duckdb/duckdb/pull/5044">Developed for DuckDB</a> in 2022.</p>
<p><strong>Elf</strong>: A careful mathematical analysis gives a way for even more efficient encoding for the xorred values.
The compression ratio is a bit better than with Chimp/Chimp128 and the performance is worse.
<a href="http://kangry.net/paper/vldb2023-elf.pdf">Paper</a> from 2023.</p>
<p><strong>Camel</strong>: A new XOR-based algorithm. I haven’t studied this one enough to summarize it, but it is included here for the sake of completeness.
<a href="https://dl.acm.org/doi/10.1145/3698802">Paper</a> from 2024, but seemingly not open access.</p>
<p><strong>Pseudodecimal Encoding (PDE):</strong> Unlike the previous algorithms, this one is not XOR-based.
The key insight is that a lot of float data is originally fixed-point decimal data and it’s more efficient to store it as a tuple of significant digits and a decimal exponent. Publishes as part of the <a href="https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/papers/btrblocks.pdf">BtrBlocks columnar data format</a> in 2023.</p>
<p><strong>Adaptive Lossless floating-point Compression (ALP)</strong>: ALP starts with the same idea as PDE but it is designed for vectorized execution, yielding much better performance. It also has an alternative scheme for storing the values that do not compress well with PDE, hence being called <em>adaptive</em>. <a href="https://dl.acm.org/doi/pdf/10.1145/3626717">Paper</a> from 2023.</p>
Storing timeseries data
https://quanttype.net/p/storing-timeseries-data/
Fri, 28 Mar 2025 00:00:00 +0000https://quanttype.net/p/storing-timeseries-data/<aside><em>Nota bene:</em> This is a notebook post. I'm jotting down
some notes, often based on an ongoing project I'm working on.
There will be less context and explanation than in my normal posts.
</aside>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/bodom_hu_fb109cde2aaec395.webp" type="image/webp" />
<img src="https://quanttype.net/images/bodom.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>What are your options for a file format if you want to store timeseries data?</p>
<p><strong>CSV or JSON lines:</strong>
The simple, stupid option which is not to be overlooked.
It works as long as you don’t have too much data or too stringent performance requirements.</p>
<p><strong>Parquet:</strong>
<a href="https://parquet.apache.org/">Apache Parquet</a> is the industry standard for columnar data used by data lakes and similar.
Possible alternatives include <a href="https://en.wikipedia.org/wiki/Apache_ORC">ORC</a>; however, it seems that ORC’s popularity is waning.</p>
<p><strong>Arrow:</strong> <a href="https://arrow.apache.org/">Apache Arrow</a> is an <em>memory</em> format for columnar data.
It’s great, but it’s not designed for efficient long-term storage.</p>
<p><strong>DuckDB:</strong> <a href="https://duckdb.org/">DuckDB</a> is an embedded databased focused on columnar data.
Like SQLite, it can be a relevant option for storing data.
Since version 0.10, they are promising <a href="https://duckdb.org/docs/stable/internals/storage#backward-compatibility">backwards compatibility</a>
for files.</p>
<p><strong>The next-generation columnar formats:</strong> <a href="https://github.com/lancedb/lance">Lance</a> promises to better suited for ML than Parquet.
<a href="https://github.com/spiraldb/vortex">Vortex</a> just promises to be all-around better than Parquet.
<a href="https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/papers/btrblocks.pdf">BtrBlocks</a> and <a href="https://www.vldb.org/pvldb/vol16/p2132-afroozeh.pdf">FastLanes</a> are more academic projects.</p>
<p><strong>Roll your own:</strong> The fun option.</p>
<p><em>Photo:</em> A small isle on frozen Lake Bodom in Espoo.</p>
Leader election with S3 and If-Match
https://quanttype.net/p/leader-election-with-s3-and-if-match/
Tue, 25 Feb 2025 00:00:00 +0000https://quanttype.net/p/leader-election-with-s3-and-if-match/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/saareke_hu_e6f66c0eab637447.webp" type="image/webp" />
<img src="https://quanttype.net/images/saareke.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>Let’s implement leader election using Amazon S3’s <code>If-Match</code> condition by building a distributed lock with it.</p>
<p>In August 2024, Gunnar Morling published <a href="https://www.morling.dev/blog/leader-election-with-s3-conditional-writes/">a blog post</a> that shows how to do it with the <code>If-None-Match</code> condition. Back then, <code>If-Match</code> had not yet been released.
This post shows another way to solve the same problem.</p>
<p>The post is intended to stand on its own so you don’t need to read Gunnar’s post first. But do read it as well to see how the solutions compare!</p>
<h2 id="whats-if-match">What’s If-Match</h2>
<p><a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html"><code>PutObject</code></a> is the API call that you use to upload data to Amazon S3. By default, the <code>PutObject</code> calls are <em>upserts</em>: they will replace the object contents or create an object if one does not already exist.</p>
<p>In 2024, Amazon introduced two conditions for the PutObject calls <code>If-Match</code> (<a href="https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-functionality-conditional-writes/">announcement</a>) and <code>If-None-Match</code> (<a href="https://aws.amazon.com/about-aws/whats-new/2024/08/amazon-s3-conditional-writes/">announcement</a>). They allow you to restrict the behavior in the following ways:</p>
<ul>
<li>If you set <code>If-None-Match: *</code>, the call will only succeed if the object does not already exist.</li>
<li>If you set <code>If-Match: <value></code>, the call will only succeed if the object exists and its content has the matching entity tag (ETag) value. Entity tag is essentially checksum for the object content.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
</ul>
<p><code>DeleteObject</code> also takes the <code>If-Match</code> condition, so you can delete an object only if it has matching ETag.</p>
<p>If the call fails, you’ll get <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-writes.html#conditional-error-response">a 412 error response</a> (or, in some cases, another 4xx error).</p>
<p>Together with S3’s consistency guarantees these conditions allow you to do <a href="https://en.wikipedia.org/wiki/Compare-and-swap"><em>compare-and-swap</em> (CAS)</a> operations.
They are a key building block for distributed systems.</p>
<h2 id="whats-leader-election">What’s leader election?</h2>
<p>Many distributed systems require designating one of the nodes as the leader.
Typically the leader accepts the write requests from the clients and then sends them to the other nodes that process read requests.</p>
<p>How do the nodes choose the leader? Martin Kleppmann in <em>Designing Data-Intensive Applications</em> writes:</p>
<blockquote>
<p>One way of electing a leader is to use a lock: every node that starts up tries to acquire the lock, and the one that succeeds becomes the leader.</p>
</blockquote>
<p>If we can build a distributed lock, we can perform leader election. Let’s see how to do that on S3.</p>
<h2 id="the-locking-protocol">The locking protocol</h2>
<p>We will use a single object in the bucket for locking. Let’s call it <code>lock</code>. It will be a JSON blob that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"expires_at"</span><span class="p">:</span> <span class="mf">1740151473.206179</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here <code>expires_at</code> is a timestamp in seconds since the UNIX epoch for when the lock expires.</p>
<p>To acquire the lock, the nodes do the following.</p>
<ol>
<li>Read the contents of <code>lock</code>. If the object does not exist, there’s no lock and we can jump to step 3.</li>
<li>If <code>expires_at</code> is in the past, the lock has expired and we can continue. Otherwise acquiring the lock has failed.</li>
<li>Put a new version of <code>lock</code> with the desired expiration time and with one of the conditions:
<ul>
<li>If <code>lock</code> existed in step 1, use <code>If-Match</code> with its ETag value.</li>
<li>If <code>lock</code> did not exist in step 1, use <code>If-None-Match</code>.</li>
</ul>
</li>
</ol>
<p>If the put in step 3 succeeds, the node has acquired the lock.</p>
<p>S3 has <a href="https://aws.amazon.com/blogs/aws/amazon-s3-update-strong-read-after-write-consistency/">strong read-after-write consistency</a>, so if there is a lock, in step 1 every node is guaranteed to see up-to-date version of the lock data. In step 3, the use of the conditions guarantees that only one node will succeed at acquiring the lock.</p>
<p>If the leader wants to release the lock, it can delete the object using If-Match with the ETag value received in step 3.</p>
<h2 id="fencing-tokens">Fencing tokens</h2>
<p>The elephant in the room is that this relies on the nodes having their clocks in sync, which is a famously difficult problem. Consider what happens if the leader’s clock is behind the others or the clock of one of the secondaries is ahead the others: the leader thinks it still holds the lock while the secondary thinks it has expired.
If the secondary now grabs the lock, the former leader can end up issuing <em>zombie requests</em>.</p>
<p>In his post <a href="https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html">How to Distributed Locking</a>, Martin Kleppman explains that you can use fencing tokens to solve the issue.
Fencing token is a number that increases every time a node acquires the lock.
The token should then be included in the requests to the system that we hold the lock over, and it should track the highest token it has seen and reject the requests with lower tokens.
This filters out the zombie requests.</p>
<p>In our case, even <code>expires_at</code> could work as a fencing token if the lock duration is always the same.
The protocol guarantees that it will always increase.</p>
<p>However, we do not have to make the lock duration fixed.
We can add another field <code>token</code> to the JSON object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"expires_at"</span><span class="p">:</span> <span class="mf">1740151473.206179</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"token"</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>token</code> is a number, starting at zero, that should be incremented every time the lock is acquired.
The node acquiring the lock reads it in step 1 and it can increase it in step 3.</p>
<p>Releasing the lock by deleting object does not work anymore as that would reset the token.
You can release the lock by setting <code>expires_at</code> to zero without incrementing <code>token</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"expires_at"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"token"</span><span class="p">:</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="python-implementation">Python implementation</h2>
<p>Here’s a basic implementation in Python using boto3.
Adding support for the fencing tokens and releasing the lock is left as an exercise for the reader.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">dataclasses</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">UTC</span><span class="p">,</span> <span class="n">datetime</span><span class="p">,</span> <span class="n">timedelta</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TYPE_CHECKING</span><span class="p">,</span> <span class="n">Self</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">boto3</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">botocore.exceptions</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">TYPE_CHECKING</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="kn">from</span> <span class="nn">mypy_boto3_s3.client</span> <span class="kn">import</span> <span class="n">S3Client</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">s3_client</span><span class="p">:</span> <span class="s2">"S3Client"</span> <span class="o">=</span> <span class="n">boto3</span><span class="o">.</span><span class="n">client</span><span class="p">(</span><span class="s2">"s3"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@dataclass</span><span class="p">(</span><span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">LockData</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">expires_at</span><span class="p">:</span> <span class="nb">float</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">to_json</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">dataclasses</span><span class="o">.</span><span class="n">asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nd">@classmethod</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">from_json</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Self</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="bp">cls</span><span class="p">(</span><span class="o">**</span><span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">acquire_lock</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">s3_client</span><span class="p">:</span> <span class="s2">"S3Client"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">bucket</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">key</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">"lock"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">expires_in</span><span class="p">:</span> <span class="n">timedelta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mi">60</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"""Try to acquire a lock using S3 as the coördination mechanism.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Args:
</span></span></span><span class="line"><span class="cl"><span class="s2"> s3_client: boto3 S3 client
</span></span></span><span class="line"><span class="cl"><span class="s2"> bucket: S3 bucket name
</span></span></span><span class="line"><span class="cl"><span class="s2"> key: S3 object key
</span></span></span><span class="line"><span class="cl"><span class="s2"> expires_in_seconds: Lock timeout
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"> Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2"> bool: True if the lock was acquired, False otherwise
</span></span></span><span class="line"><span class="cl"><span class="s2"> """</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">existing_lock</span> <span class="o">=</span> <span class="n">s3_client</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">Bucket</span><span class="o">=</span><span class="n">bucket</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">Key</span><span class="o">=</span><span class="n">key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">except</span> <span class="n">botocore</span><span class="o">.</span><span class="n">exceptions</span><span class="o">.</span><span class="n">ClientError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">response</span><span class="p">[</span><span class="s2">"Error"</span><span class="p">][</span><span class="s2">"Code"</span><span class="p">]</span> <span class="o">==</span> <span class="s2">"NoSuchKey"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">existing_lock</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">existing_lock</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">existing_data</span> <span class="o">=</span> <span class="n">LockData</span><span class="o">.</span><span class="n">from_json</span><span class="p">(</span><span class="n">existing_lock</span><span class="p">[</span><span class="s2">"Body"</span><span class="p">]</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">UTC</span><span class="p">)</span><span class="o">.</span><span class="n">timestamp</span><span class="p">()</span> <span class="o"><=</span> <span class="n">existing_data</span><span class="o">.</span><span class="n">expires_at</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">condition</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"IfMatch"</span><span class="p">:</span> <span class="n">existing_lock</span><span class="p">[</span><span class="s2">"ETag"</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">condition</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"IfNoneMatch"</span><span class="p">:</span> <span class="s2">"*"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">lock_data</span> <span class="o">=</span> <span class="n">LockData</span><span class="p">(</span><span class="n">expires_at</span><span class="o">=</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">UTC</span><span class="p">)</span> <span class="o">+</span> <span class="n">expires_in</span><span class="p">)</span><span class="o">.</span><span class="n">timestamp</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">s3_client</span><span class="o">.</span><span class="n">put_object</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">Bucket</span><span class="o">=</span><span class="n">bucket</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">Key</span><span class="o">=</span><span class="n">key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">Body</span><span class="o">=</span><span class="n">lock_data</span><span class="o">.</span><span class="n">to_json</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl"> <span class="o">**</span><span class="n">condition</span><span class="p">,</span> <span class="c1"># type: ignore[arg-type]</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">except</span> <span class="n">botocore</span><span class="o">.</span><span class="n">exceptions</span><span class="o">.</span><span class="n">ClientError</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">error</span><span class="o">.</span><span class="n">response</span><span class="p">[</span><span class="s2">"Error"</span><span class="p">][</span><span class="s2">"Code"</span><span class="p">]</span> <span class="ow">in</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"ConditionalRequestConflict"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"PreconditionFailed"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># We could alternatively retry on ConditionalRequestConflict (409)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span>
</span></span></code></pre></div><p>Here’s another exercise for the reader:
The <code>lock</code> object does not include information about who is holding the lock as it’s not necessary for the protocol.
However, it would be handy in a real-world implementation in case you ever need to debug this.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/vepsu_hu_16a06b18ea1f3115.webp" type="image/webp" />
<img src="https://quanttype.net/images/vepsu.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<h2 id="does-this-make-sense">Does this make sense?</h2>
<p>What’s nice about this compared to Gunnar’s version is that there’s no need for a background process to delete the stale lock objects.
Gunnar’s design creates a new object every time a lock is acquired but in this version, there’s only a single object that gets modified.</p>
<p>However, with both designs you have to ask whether they make sense in the real world.
As <a href="https://quanttype.net/posts/2024-09-02-weeknote-3-object-storage.html">I’ve mentioned before</a>,
while S3 storage is fairly inexpensive, the requests are not cheap: in the standard tier and <code>us-east-1</code> region, <a href="https://aws.amazon.com/s3/pricing/">PUTs cost $0.005 per 1000 requests</a>
and GETs cost $0.0004 per 1000 requests. The latencies are in double-digit milliseconds.
S3 Express One Zone makes the requests only 2x cheaper, so it does not materially change the situation.</p>
<p>This means that if you’re looking to build a high-performance, low-cost distributed lock, S3 is not going to be your first choice.
You would probably use it because you’re already using S3 for something else and you want to hold a lock over S3 resources.
Unfortunately S3 does not support fencing tokens for <code>PutObject</code> calls, which limits the usefulness of this approach.</p>
<h2 id="meta">Meta</h2>
<p>This is a companion post for my lightning talk <a href="https://www.hytradboi.com/2025/46867aad-ddc3-4e7a-a530-941e1c363bba-why-s3s-conditional-writes-made-people-excited">Why S3’s conditional writes made people excited</a>
at HYTRADBOI that shows how to use <code>If-None-Match</code>.
It presents an idea similar to Gunnar’s and to what <a href="https://www.vldb.org/pvldb/vol13/p3411-armbrust.pdf">Delta Lake</a> uses in practice.</p>
<p><strong>Talk errata:</strong> The error response for failing condition is <code>412</code>, not <code>421</code> as claimed in the talk.</p>
<p><em>Thanks to Joel Kaasinen, Juuso Takalainen, Iivari Äikäs, Waltteri, and Rishit for giving feedback on the talk
and thanks to Joel Kaasinen for feedback on this post. Any mistakes are my own.</em></p>
<p><strong>Photos:</strong> The first one shows a rock and a tree at the frozen Lake Meiko in Kirkkonummi, Finland on a cloudy winter day.
The second one is a cliff at Vepsu, a small island in the sea in front of Hamina, Finland.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>According to <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html#API_PutObject_ResponseSyntax">S3 docs</a>, it’s sometimes but not always the MD5 digest of the object. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
New winds
https://quanttype.net/p/new-winds/
Fri, 31 Jan 2025 00:00:00 +0000https://quanttype.net/p/new-winds/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/merikoski_hu_247d1cdeb6260d59.webp" type="image/webp" />
<img src="https://quanttype.net/images/merikoski.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>In my <a href="https://quanttype.net/posts/2025-01-08-yearnote-2024.html">last post</a>, I wrote about my plans for this year:</p>
<blockquote>
<p>This time I’m trying to be more intentional, especially career-wise.</p>
</blockquote>
<p>I concluded that it is time for me to move on from my position as a software engineer at Oura Health.
I had a great two and a half years there, building and operating the backend-slash-database that is the main data store of the company, and I learned a lot about Python, Rust, AWS services, cloud databases, and scaling backend systems.</p>
<p>However, I want to focus even more closely on how data is stored in the cloud.
Therefore I’m planning to venture out as an independent expert.</p>
<p>More on that later. Meanwhile, if you’ve got exciting opportunities for me,
<a href="mailto:[email protected]">email me</a> or <a href="https://fi.linkedin.com/in/miikka-koskinen-a20006239">connect on LinkedIn</a>.</p>
<p><strong>Photo:</strong> A view of Merikoski power plant in Oulu in early January.</p>
Yearnote 2024
https://quanttype.net/p/yearnote-2024/
Wed, 08 Jan 2025 00:00:00 +0000https://quanttype.net/p/yearnote-2024/<p>It’s 2025 now. This century is now 25% complete!
As is <a href="https://quanttype.net/posts/2024-01-08-yearnote-2023.html">my tradition</a>, I’m going to reflect on the year gone by.</p>
<p><em>On the photos: there’s one photo for each month, in chronological order.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-01_hu_32e7286d56871a88.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-01.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-02_hu_35792263d5e70be8.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-02.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="software-engineering">Software engineering</h2>
<p>At work, I continued to build cloud backends to manage data.
I ended up spending a lot of time with Rust, OpenTelemetry, Docker, GitHub Actions (GHA).
Out of these technologies, learning Rust was great and trying to use OpenTelemetry only led to frustration.</p>
<p>Docker and GHA got the job done. Later I posted about <a href="https://quanttype.net/posts/2024-12-08-weeknote-17-caching-docker-builds.html">how to cache Docker builds</a>.</p>
<p>Later in the year, I spent some time on clarifying how we do <a href="https://quanttype.net/posts/2024-08-18-weeknote-1-schema-evolution.html">schema evolution</a>. Then I switched to building <a href="https://quanttype.net/posts/2024-09-08-weeknote-4-debugging-tools.html">developer tooling</a>.
That was a nice change of pace. Building tools is fun and when you build internal tooling,
your end users are right there.</p>
<p>The tooling project led me to revisit Python packaging. <a href="https://quanttype.net/posts/2024-11-24-weeknote-15-tech-radar.html#hold">It didn’t go well</a>.</p>
<h2 id="rust-in-production">Rust in production</h2>
<p>The Rust project was interesting. We implemented a new web backend in Rust and shipped it to production.
We managed to run into a lot of rough spots in the Rust web dev ecosystem.</p>
<p>For starters, async Rust is Rust in hard mode and it’s also work-in-progress – for example,
<a href="https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html"><code>async fn</code> was allowed in traits</a>
only right when we were starting the project. This was the first real Rust project for all of us,
so there was a bit of a learning curve.</p>
<p>Then we ran into numerous <a href="https://github.com/launchbadge/sqlx">sqlx</a> issues.
The <a href="https://github.com/open-telemetry/opentelemetry-rust">OpenTelemetry crates</a> are unstable and there are regular breaking changes
which increased the maintenance burden.
If you’re used to Python testing tools, Rust just isn’t on the same level - we struggled with
fixtures and with mocking AWS services.</p>
<p>Not everthynig was bad.
For routing we used <a href="https://github.com/poem-web/poem">Poem</a>, which got the job done even if it was pretty verbose.
<a href="https://www.cargo-lambda.info/">Cargo Lambda</a> was also great – it just worked and even cross-compilation
works out of the box.</p>
<p>It wasn’t the smoothest project, but at least we learned a lot.
Later we were able to use that knowledge to introduce Rust into another system where it gets to shine for real.</p>
<h2 id="software-engineering-community">Software engineering community</h2>
<p>I didn’t have much time for open source, but I did create <a href="https://codeberg.org/miikka/paketoi">paketoi</a>,
a command-line tool for building Python deployment packages for AWS Lambda. <a href="https://quanttype.net/posts/2024-04-23-paketoi-0.1.html">The announcement post</a> explains the motivation.</p>
<p>I attended two conferences, <a href="https://quanttype.net/posts/2024-09-25-weeknote-6-heart-of-clojure.html">Heart of Clojure</a>
and <a href="https://quanttype.net/posts/2024-10-13-weeknote-9-eurorust-2024.html">EuroRust 2024</a>. In-person conferences are great except that I got predictably sick afterwards.</p>
<p>At Heart of Clojure, I gave a lightning talk where I said that you should blog more. You should blog more!</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-03_hu_f8b56b55d5652a07.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-03.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-04_hu_30ebb2d98ca8d40a.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-04.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="blogging">Blogging</h2>
<p>In August, I started posting <a href="https://quanttype.net/tags/weeknote.html">weeknotes</a>.
Having a weekly writing routine was a big success.
Even if most of the posts are nothing special, it got my writing engine going.
Many of the posts sparked nice conversations with colleagues, friends, and Internet strangers.</p>
<p>This year, my most read post was <a href="https://quanttype.net/posts/2023-10-31-do-not-use-requirements.txt.html"><em>Do not use requirements.txt</em></a> (my most popular post ever!). Out of the articles I’ve posted this year, <a href="https://quanttype.net/posts/2024-09-25-weeknote-6-heart-of-clojure.html">the trip report on Heart of Clojure</a> was the most popular one.</p>
<p>I also announced that I’m starting a newsletter for my outdoors adventures called <a href="https://smallrapids.net/">Small Rapids</a>.
The first post is still waiting for itself, but I promise it is coming soon! It will be about tour skating and ice safety.</p>
<p>It was great to get back to writing in public and I want continue it in 2025.
I’m hoping to write a couple of better-researched and better-edited posts.</p>
<h2 id="microblogging">Microblogging</h2>
<p>I’m <a href="https://mastodon.social/@Miikka/">active on Mastodon</a>. Check out my most banger posts:</p>
<ol>
<li><a href="https://mastodon.social/@Miikka/113685312562758126">My favorite Wikipedia illustration</a></li>
<li><a href="https://mastodon.social/@Miikka/113492596590845476">HTML cable car</a></li>
<li><a href="https://mastodon.social/@Miikka/112757676373796883">Mt. Wank cable car</a></li>
</ol>
<p>Looks like Fediverse enjoys cable cars.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-05_hu_922752c8b59fecae.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-05.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-06_hu_14df014c6f41978a.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-06.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="outdoors-life">Outdoors life</h2>
<p>It was a good outdoors year. It began with a cold winter in Helsinki, which meant that I got plenty of good opportunities to skate on the sea ice.</p>
<p>In the spring, I ran regularly. This paid off when I decided on a whim to go hike in Urho Kekkonen National Park after Midsummer. Never has hiking been so easy! I spend a few days there, visiting Paratiisikuru and taking the high route whenever possible.</p>
<p>It was actually my first hike that was over 100 km.
I was going to do one already in 2020 - I was hoping to hike Kungsleden in Sweden - but I had to cancel it due to COVID-19.
Back then it felt like such a big goal. Now it was just something I did without much thought.</p>
<h2 id="paddling">Paddling</h2>
<p>My big summer project was to learn to roll a sea kayak. I started by taking a course by <a href="https://www.rollingkayak.fi/">Anssi Nupponen</a>. Anssi’s teaching was great, but two days was not enough for me to learn even a butterfly roll. However,
I learned enough to practice myself.</p>
<p>After a bunch of sessions, I managed to do sweep roll on one side.
Later at the rescue practice camp of my kayak club, I managed to roll the kayak reliably in pretty big waves – big for Baltic Sea, that is. That felt great.</p>
<p>Maybe in 2025 I will learn to do it from the other side. It would be fun to learn some other rolls, too.</p>
<h2 id="climbing">Climbing</h2>
<p>In the spring, I learned to climb on top rope. I didn’t go climbing outdoors too many times, but <a href="https://mastodon.social/@Miikka/113258489685472139">I had good time when I did</a>. This year, I hope to learn to lead climb.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-07_hu_f45ba7f8764233cf.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-07.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-08_hu_804df2e3687234cd.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-08.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="best-of-2024">Best of 2024</h2>
<ul>
<li><strong>Best new album:</strong> <a href="https://wejazzrecords.bandcamp.com/album/lehto-korpi"><em>Lehto / Korpi</em></a> by Pauli Lyytinen. Who could not love saxophone improvisation on top of bird song field recordings?</li>
<li><strong>Best gig:</strong> The secret gig at <a href="https://wejazz.fi/2024/">We Jazz</a> where Antti Lötjönen, Heli Hartikainen and Aino Juutilainen played together. The atmosphere of the gig was something else, so focused. You just had to be there.</li>
<li><strong>Most interesting book read:</strong> <em>Raukoilla rajoilla</em> by Markku Eskelinen. It’s an iconoclastic history of Finnish prose and it filled me with new interest in Finnish literature. Eskelinen does not hold it in very high regard, but the counter-examples he points out are interesting.</li>
<li><strong>New favorite writer:</strong> Lydia Davis. I read her latest book <em>Our Strangers</em> and her super-short short stories observing everyday life are wonderful.</li>
</ul>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-09_hu_35255c991956aa1.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-09.jpg" width="600" height="497.5" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-10_hu_25e55b3408a12417.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-10.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="what-about-2025">What about 2025?</h2>
<p>Last year I wrote:</p>
<blockquote>
<p>What about 2024? I don’t know yet. I’ll figure it out.</p>
</blockquote>
<p>I did not explicitly set out to figure it out and the result was that I did not figure it out.
It left me feeling aimless. This time I’m trying to be more intentional<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>,
especially career-wise. I’m asking myself where I want to go. Haven’t answered it yet.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-11_hu_1b9e42dbea6c010f.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-11.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2024-12_hu_df45628b2d87a2b.webp" type="image/webp" />
<img src="https://quanttype.net/images/2024-12.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p><em>In each yearnote, I express (lack of) surprise at the current cabinet of the goverment of Finland.</em></p>
<p>A year ago I predicted that while the scandals will continue, Petteri Orpo will be able to hold his cabinet together. Looks like I was right for once!</p>
<p>What about the next year? The closer we get to the election, more likely the cabinet is to fall apart. There are two reasons for this. First, the more time goes on, the more Orpo has been able to achieve and thus it’s less of an imperative to hold together. Second, the policies of the cabinet have been incredibly unpopular and the closer we get to the election, the more anxious the parties get to distance themselves from the cabinet.</p>
<p>Orpo’s cabinet was formed in 2023 and the next parliamentary election is in 2027, so there’s still plenty of time to the next election. Thus my prediction is that Orpo’s cabinet will hold together for the next year. The turbulence from the upcoming municipal and county elections will not be enough to break it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>It’s not that you have to wait for a year to change to start being intentional. The timing just happens to coincide. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Weeknote 19: ADRs record decisions
https://quanttype.net/p/weeknote-19-adrs-record-decisions/
Sun, 22 Dec 2024 00:00:00 +0000https://quanttype.net/p/weeknote-19-adrs-record-decisions/<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/puut2_hu_fc52d793f4e58201.webp" type="image/webp" />
<img src="https://quanttype.net/images/puut2.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
This week I wrote a bunch of <em>architectural decision records</em> (ADRs) for a project I’m working on.</p>
<p>ADRs were popularized by Michael Nygard’s blog post <a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions"><em>Documenting architecture decisions</em></a>. Nygard does a great job explaining why they will be useful in the future.</p>
<p>They will give useful context about decisions for the people who weren’t around when the decisions were made, and also for people who were there but already forgot about it. This in turns allows being intentional about accepting or changing decisions.</p>
<p>However, they have benefits already today.</p>
<p><strong>Writing things down clarifies your thoughts.</strong> I keep banging this drum, but it’s worth mentioning again. As you write an ADR, you will have to work through the details. This will clarify your thoughts and reveal the flaws in your arguments.</p>
<p><strong>An ADR makes it clear if a decision was made.</strong> ADR is a kind of a design document, but not all processes for design documents track their status. This can become a problem because while you can find the old docs, you don’t know what was decided about them and whether they were ever implemented. This is bound to happen if nobody ever explicitly makes a decision about them.</p>
<p>ADRs can act as a forcing function. Nygard’s ADR template has a <em>Status</em> field. He suggests that the value can be “proposed”, “accepted”, “deprecated”, or “superseded”. The template also has a section called “Decision” that spells out the decision. This should make the situation clear. You would not mark an ADR as accepted unless a decision was made, right?</p>
<p>You still have to keep publishing new ADRs and mark the old ones as deprecated as you go along, but now you’ve got a process for it.</p>
<h2 id="blog-meta">Blog meta</h2>
<p>This is going to be the last weeknote of the year. I’m going to be off for a vacation. The next post is going to be
my yearly yearnote for 2024. Happy holidays everyone!</p>
<p><strong>Photo:</strong> A pile of firewood. ADRs are form a decision <em>log</em>, I suppose.</p>
Weeknote 18: Code comments
https://quanttype.net/p/weeknote-18-code-comments/
Sun, 15 Dec 2024 00:00:00 +0000https://quanttype.net/p/weeknote-18-code-comments/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lumipuut2_hu_9a224ba5c813f3a0.webp" type="image/webp" />
<img src="https://quanttype.net/images/lumipuut2.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
<p>When do you add comments to your code? Do you do it all? Everyone has seen one of these comments:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1"># increment counter by 1</span>
</span></span></code></pre></div><p>This comment is explains what is already obvious from the code, so it’s not useful.
What’s worse is when somebody then changes the code to be like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">counter</span> <span class="o">+=</span> <span class="mi">2</span> <span class="c1"># increment counter by 1</span>
</span></span></code></pre></div><p>Now the comment does not match the code anymore. People bring up examples like this when they disparage comments. However, there are other kinds of code comments that are much more valuable.</p>
<h2 id="explain-why-the-code-looks-wrong">Explain why the code looks wrong</h2>
<p>Code should be written to be easy to understand because understanding is required for both debugging and modifying the code. This is mainly about the code itself: writing clear code, choosing module boundaries carefully, using descriptive names, and maintaining cohesive style.</p>
<p>At times, comments are needed too. In <em>A Philosophy of Software Design</em>, John Ousterhout proposes the following principle:</p>
<blockquote>
<p>Comments should describe things that aren’t obvious from the code.</p>
</blockquote>
<p>I agree. One heuristic that I’ve lately used is that <strong>you should add a comment if the code looks wrong</strong>. Think of the next person reading the code – is there anything they would think as weird or out of place? If the wrong-looking code is correct but you can’t make it look correct, you should add a comment.</p>
<p>For example, I recently ran into code that looked like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">thing1</span> <span class="o">=</span> <span class="n">create_thing</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">thing2</span> <span class="o">=</span> <span class="n">create_thing</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">thing3</span> <span class="o">=</span> <span class="n">create_thing</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">thing4</span> <span class="o">=</span> <span class="n">create_thing</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">thing</span> <span class="ow">in</span> <span class="p">[</span><span class="n">thing1</span><span class="p">,</span> <span class="n">thing2</span><span class="p">,</span> <span class="n">thing3</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl"> <span class="n">process_thing</span><span class="p">(</span><span class="n">thing</span><span class="p">)</span>
</span></span></code></pre></div><p>Isn’t there something odd about this? For some reason, <code>thing4</code> is not processed. Maybe the author of the code has accidentally left it out. Should you go and add it to the list?</p>
<p>In this case, there was a good reason for the omission and the code itself did not need changes. We added a comment, though, explaining the reason right next to the for loop. This way, the next reader will not need to question it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># `thing4` is not included because it will be processed by another component</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">thing</span> <span class="ow">in</span> <span class="p">[</span><span class="n">thing1</span><span class="p">,</span> <span class="n">thing2</span><span class="p">,</span> <span class="n">thing3</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl"> <span class="n">process_thing</span><span class="p">(</span><span class="n">thing</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="ghc-notes">GHC Notes</h2>
<p>Sometimes you need to explain the same thing in comments in multiple places. Glasgow Haskell Compiler (GHC) has a nice convention sometimes known as <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/coding-style#2-using-notes">GHC notes</a>. They have a syntax for the long comments that can to be referred to from other places.</p>
<p>My Python adaptation of the style looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># Note [This is a note]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ---------------------</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Here goes the long explanation I want to refer to some other place in the code.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># It is usually multiple lines long.</span>
</span></span></code></pre></div><p>Then you can refer to it from another comment like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">do_a_thing</span><span class="p">()</span> <span class="c1"># See Note [This is a note]</span>
</span></span></code></pre></div><p>I’ve used this a bit in the last few months and it feels promising.
Now there’s only one place for the information, which makes it easier to keep it up to date.</p>
<p>It would be great to have editor support for this so you could jump to the actual note from the reference.
Unfortunately so far I haven’t found anything suitable for VS Code.</p>
<p><strong>Photo:</strong> A snow-covered small tree on the shore of Lake Vähä-Parikas.</p>
Weeknote 17: Caching Docker builds on GitHub Actions
https://quanttype.net/p/weeknote-17-caching-docker-builds/
Sun, 08 Dec 2024 00:00:00 +0000https://quanttype.net/p/weeknote-17-caching-docker-builds/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/vahaparikas_hu_562aeacd69befef1.webp" type="image/webp" />
<img src="https://quanttype.net/images/vahaparikas.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
<p>In the past few weeks, I’ve spent a lot of time building Docker images on GitHub Actions.
Anyone who has done it knows what it is like: I’ve been <a href="https://xkcd.com/303/">waiting a lot</a>.</p>
<p>To avoid waiting, you’ll want to set up build caching. Here’s a few things I learned about it.</p>
<h2 id="building-docker-compose-targets">Building Docker Compose targets</h2>
<p>For one of our services, we have a Docker Compose based setup for running it locally.
To ensure that it continues to work, I created a CI workflow with GitHub Actions that builds the images, starts the system, and executes a few tests to check that everything is okay.</p>
<p>The first version of the build step looked like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker compose build</span><span class="w">
</span></span></span></code></pre></div><p>It worked great except for one thing: this does not cache anything between CI jobs. The way to go for caching Docker builds is to use BuildKit’s <a href="https://docs.docker.com/build/cache/backends/">nice cache backends</a>. But how to use them with Docker Compose?</p>
<p>Docker now has the <a href="https://docs.docker.com/build/bake/"><code>docker buildx bake</code></a> command for building the targets defined in a <code>docker-compose.yml</code> file. There’s an official action <a href="https://github.com/docker/bake-action"><code>docker/bake-action</code></a> for it.</p>
<p><a href="https://docs.docker.com/build/ci/github-actions/cache/#github-cache">GitHub Actions cache backend</a> (<code>type=gha</code>)
is the easiest backend to use as it needs no setup. Use the <code>set</code> input to enable it like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/bake-action@v5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">set</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd"> *.cache-from: type=gha
</span></span></span><span class="line"><span class="cl"><span class="sd"> *.cache-to: type=gha,mode=max</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>The <code>*.</code> wildcard means it will be enabled for all targets.</li>
<li>If you’re building a multi-stage image, you’ll want to set <a href="https://docs.docker.com/build/cache/backends/#cache-mode">the cache mode</a> to max with <code>mode=max</code> to cache the earlier stages in addition to the last one.</li>
</ul>
<p>See the <a href="https://github.com/docker/bake-action?tab=readme-ov-file#inputs">docs</a> for all the settings, but you’ll
probably want to use one or both of these:</p>
<ul>
<li>To push the images, set <code>push: true</code>.</li>
<li>To make the images available for local <code>docker</code> commands, set <code>load: true</code>.</li>
</ul>
<p>The complete test workflow could look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Test the local setup</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">push]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">bake</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/setup-buildx-action@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/bake-action@v5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">load</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">set</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd"> *.cache-from: type=gha
</span></span></span><span class="line"><span class="cl"><span class="sd"> *.cache-to: type=gha,mode=max</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Start the services</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker compose up -d --wait</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Test the services</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo TODO</span><span class="w">
</span></span></span></code></pre></div><h2 id="the-registry-backend">The registry backend</h2>
<p>When building Docker images, you can easily run into the <a href="https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy">10GB size limit</a> of GitHub Actions cache. As a workaround, you can use <a href="https://docs.docker.com/build/ci/github-actions/cache/#registry-cache">the registry backend</a> (<code>type=registry</code>) which uses a container registry as the cache backend.</p>
<p>Since you’re already on GitHub, the easiest solution can be to use GitHub Container Registry (GitHub Packages), but for example <a href="https://aws.amazon.com/blogs/containers/announcing-remote-cache-support-in-amazon-ecr-for-buildkit-clients/">AWS ECR works too</a>.</p>
<p>The setup is a bit more involved - see the docs for your registry on how to allow GitHub Actions to push there.</p>
<h2 id="gotcha-multi-arch-builds">Gotcha: Multi-arch builds</h2>
<p>If you’re caching a multi-arch build, note that it does not work out of the box, at least not with <code>type=registry</code>.
<a href="https://github.com/docker/buildx/discussions/1382#discussioncomment-6252049">You have to jump through hoops to cache it</a>.</p>
<p><strong>Photo:</strong> An Independence Day view on the snow-covered lake Vähä-Parikas in Vihti, Finland. It is a color photo.</p>
Weeknote 16: Late code review
https://quanttype.net/p/weeknote-16-late-code-review/
Sun, 01 Dec 2024 00:00:00 +0000https://quanttype.net/p/weeknote-16-late-code-review/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/wankview_hu_d06bb6c7a095967b.webp" type="image/webp" />
<img src="https://quanttype.net/images/wankview.jpg" width="600" height="400" fetchpriority="high" />
</picture>
</figure>
<p>This week I reviewed my notes about <a href="https://quanttype.net/tags/code-review.html">code review</a> and noticed there’s something I haven’t written about:
<strong>code review often happens too late</strong>.</p>
<aside><em>Nota bene:</em> When I talk about code review, I talk about it in the context
of corporate software engineering. Other contexts, such as open-source projects, have different
problems and solutions.
</aside>
<p>Typically a developer requests code review for their pull request once it’s pretty much done.
This is too late.</p>
<p>If you have developed a big change and the reviewer thinks the whole approach is wrong,
it’s wasteful to throw the whole change away at this point, and it’s not fun feedback to get.
This puts a limit on how big changes the reviewer can ask for in the review.</p>
<p>Another issue is that if in your mind, as the author, the pull request is finished and ready to merge,
then it’s not fun when somebody pops up to ask you to make changes. This is especially
true <a href="https://quanttype.net/posts/2019-12-05-keeping-code-review-fast.html">if your code review process is slow</a> and
you have already moved on to work on something else.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="dealing-with-it">Dealing with it</h2>
<p>How to counteract it? You need to get the reviewer involved more early.
Here are a few things that I’ve tried:</p>
<p><strong>Talk about what you’re doing.</strong> <a href="https://drmaciver.substack.com/p/telegraph-your-moves">Telegraph your moves</a>.
Tell your colleagues in the daily standup what you’re working on and <em>how</em> you’re going about it.
This is simple and usually works well enough.</p>
<p><strong>Split up your work.</strong> Do multiple pull requests that build up to the goal you’re working towards.
Smaller pull requests are easier to review, too. This has worked well, too, although the overhead in the review process will slow you down.</p>
<p><strong>Request an early review.</strong> I’ve sometimes asked my colleagues to review the general approach taken in PR
when it’s still a draft. It has not worked very well.
Either people do not say anything or then they review the PR as if it was already ready.
I still think this could work, but you need to be clear about what kind of feedback you’re looking for.</p>
<h2 id="bottom-line">Bottom line</h2>
<p>Code review is an opportunity for collaboration and it works much better if you embrace it as such,
both as an author and a reviewer.</p>
<p><strong>Photo:</strong> I’ve temporarily ran out of seasonal photos, so here’s a view from summer from top the Wank near Garmisch-Partenkirchen.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Starting new work when you’ve got old work in progress is questionable but common. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Weeknote 15: Technology radar
https://quanttype.net/p/weeknote-15-tech-radar/
Sun, 24 Nov 2024 00:00:00 +0000https://quanttype.net/p/weeknote-15-tech-radar/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaata_maassa_hu_cc6974a34b28bba3.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaata_maassa.jpg" width="600" height="450" fetchpriority="high" />
</picture>
</figure>
<p>Every time ThoughtWorks publishes their <a href="https://www.thoughtworks.com/radar">Technology Radar</a>,
I take a look. It’s a nice source for finding out about new technologies and
I think the Hold/Assess/Trial/Adopt system makes sense even if I often disagree
with the classification choices.</p>
<p>At work, I’ve been building some internal tooling
for working with timeseries data and I’ve had a chance to try some new things.
I decided to make my own mini-radar based on these experiences.</p>
<h2 id="adopt">Adopt</h2>
<p><em>These are great, you should use them.</em></p>
<p><strong><a href="https://pola.rs/">Polars</a></strong> is a dataframe library for Python and Rust. A dataframe is an abstraction
for tabular data - in my mind it’s similar to a database table. I think they
originally come from R.</p>
<p>While <a href="https://pandas.pydata.org/">pandas</a> has long been the go-to Python library
for dataframes, Polars feels fresh and streamlined.
I’ve found it to be both fast and easy to use. The API makes sense to me and it’s easy to get data into and out of Polars.
It has been a great choice for working with generic timeseries data.</p>
<p><strong><a href="https://duckdb.org/">DuckDB</a></strong> is “SQLite for columnar data”. It’s an embeddable single-file database like SQLite,
but it’s geared towards analytics type of queries. My favorite feature is that it has support
for a number of <a href="https://duckdb.org/docs/data/data_sources">data sources</a> so if you need to
analyze, say, a bunch of Parquet files on S3, DuckDB can do it out of the box. It’s fast, too,
and the <code>duckdb</code> command-line tool is nice to use.</p>
<p>I can’t believe I haven’t mentioned DuckDB on the blog before!</p>
<h2 id="trial">Trial</h2>
<p><em>These are pretty good, but I’ve got some qualms about them.</em></p>
<p><strong><a href="https://htmx.org/">htmx</a></strong> is a JavaScript library that makes it easy to use AJAX without writing any code.
I’m not sure how to best explain it, but <a href="https://htmx.org/docs/#introduction">check out the introduction</a>.</p>
<p>It’s great for when you have made a traditional server-rendered web application
and need to sprinkle just a little bit of JavaScript on it to make it nice to use.
I’m putting it under “Trial” because while it is handy, I don’t understand when and how to switch to
something more advanced.</p>
<h2 id="assess">Assess</h2>
<p><em>There’s show a lot of promise, but I haven’t yet tried them seriously.</em></p>
<p><strong><a href="https://github.com/astral-sh/uv">uv</a></strong> is a Python package management tool. Initially it started as a <code>pip</code> replacement,
but it has recently gained full <a href="https://docs.astral.sh/uv/guides/projects/">project support</a>.
It’s snappy and it’s gaining features fast.
I’ve previously <a href="https://quanttype.net/posts/2023-10-31-do-not-use-requirements.txt.html">recommended Poetry</a>
for Python project management, but uv could be a better choice already since it’s both faster
and more compatible with the rest of Python ecosystem than Poetry is.</p>
<p>I’m putting uv under “Assess” since I have not yet had a chance to try it in a big project.</p>
<h2 id="hold">Hold</h2>
<p><em>I don’t think these work very well in practice.</em></p>
<p><strong>Python for command-line tools</strong>. Due to packaging difficulties,
I’ve found it difficult to ship a command-line tool written in Python to software engineers who are not Python developers.
You need to get all the dependencies installed somehow and all approaches seem to run into trouble.</p>
<p>If I were to do it again, I would seriously consider Rust or Go where there’s good support for shipping software as single binary.</p>
<h2 id="in-conclusion">In conclusion</h2>
<p>This is what’s on my radar - what about yours?</p>
<hr>
<p>Long-time readers may have noticed that I haven’t posted about outdoors hobbies
in a while. That’s because I have wanted to focus this blog on software engineering.</p>
<p>I’m starting a newsletter about my outdoors pursuits such as kayaking and tour skating.
It’s called <a href="https://smallrapids.net/">Small Rapids</a>.
The first post is coming soon - subscribe so you won’t miss it!</p>
Weeknote 14: Throwing it away
https://quanttype.net/p/weeknote-14-throwing-it-away/
Sun, 17 Nov 2024 00:00:00 +0000https://quanttype.net/p/weeknote-14-throwing-it-away/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/moss_hu_7979cb479d56724d.webp" type="image/webp" />
<img src="https://quanttype.net/images/moss.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>Apparently <a href="https://stackoverflow.com/q/78806/297366">it was Fred Brooks</a> in <em>The Mythical Man Month</em> who wrote that,
when building a new software system, you should “plan to throw one away”.</p>
<p>It sounds like great advice, but I haven’t seen anyone follow it.
I recently built a prototype of an internal tool at work and I thought this time I will throw it away.</p>
<p>The tecnology I chose was Python.
Normally when I write Python, I insist on using <a href="https://www.mypy-lang.org/">mypy</a> the type checker.
This time I didn’t use it, and I didn’t write any tests either.
It’s not code we’re going to keep if it hasn’t got tests or types, right?</p>
<p>However, I showed my prototype to a few people and some of them became early adopters and started using the tool for real work.
This meant that while I need to do big changes, I didn’t want to break it for them.
Thus I added mypy and created a basic test suite.
And boom, now we might as well keep the code.</p>
<p>I think I got the basics right, so it’s probably fine. Next time I will write the prototype in Elixir or some other language that nobody at work uses. <em>Then</em> we can’t keep it, right?</p>
<h2 id="in-other-news">In other news</h2>
<p>People seemed to be delighted by my <a href="https://mastodon.social/@Miikka/113492596590845476">HTML cable car</a>.</p>
<p><strong>Photo:</strong> A closeup of moss with needles and dead leaves on it.</p>
Weeknote 13: Deterministic Simulation Testing
https://quanttype.net/p/weeknote-13-deterministic-simulation-testing/
Sun, 10 Nov 2024 00:00:00 +0000https://quanttype.net/p/weeknote-13-deterministic-simulation-testing/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kielo_hu_1374d2f3d488cc17.webp" type="image/webp" />
<img src="https://quanttype.net/images/kielo.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>This Tuesday was the day of the first <a href="https://helsystems.xyz/">Systems from HEL</a> meetup. It’s a meetup about systems programming – there’s a bunch of systems meetups in the US and now there’s one in Helsinki, too.</p>
<p>Pekka Enberg, the founder of the meetup, was also the first person to give a presentation.
He talked about <em>deterministic simulation testing</em> (DST).
He gave an overview of the technique and demoed his prototype implementation <a href="https://github.com/penberg/hiisi">penberg/hiisi</a>.
Pekka’s company <a href="https://turso.tech/">Turso</a> has recently started using DST in anger and he shared some lessons they had learned. Pekka’s talk can be <a href="https://www.youtube.com/live/29Vz5wkoUR8">viewed on YouTube</a>.</p>
<p>A problem with systems like databases is that there are these bugs that are really difficult to trigger. Sometimes you need to do things in just the right order to trigger a bug, but in real world systems there are many sources of non-determinism: file and network I/O can be slow or fail, threads can get scheduled in different order, etc.
This kind of bugs may get triggered in production, but it’s diffult to debug them because you can’t reliably reproduce them.</p>
<p>DST’s answer to the problem is simple: take control of all sources of non-determinism and make them deterministic in the testing environment. If you abstract away the calls to a file system, the network, or a time source, you can create a simulator runtime that can deterministically mock the results and inject faults.</p>
<p>Just like in property-based testing, you can use a pseudo-random number generator (PRNG) to to generate the results and the faults. You can also use it to generate the inputs to the system such as client calls. If you re-run the test with the same PRNG seed, you should get the same results – now you can debug it. By running the system with a lot of random PRNG seeds, you get a good chance of triggering rare bugs.</p>
<h2 id="dst-in-practice">DST in practice</h2>
<p>The trouble with DST that it’s difficult to pull off. The biggest takeaway for me from Pekka’s experiences was that you don’t have to go all in to get benefits. Controlling every source of non-determinism is a lot of work, but tackling even some of them lets you find bugs. At Turso, their experience was that every time they have taught the simulator new tricks, they have found new bugs.</p>
<p>If you think it sounds like fuzzing and chaos testing in addition to property-based testing,
yup, you’re right. It combines ideas from all of them.</p>
<p>Historically FoundationDB <a href="https://apple.github.io/foundationdb/testing.html">pioneered the technique</a> about a decade ago. Right now the some of the same people are pushing the envelope with <a href="https://www.antithesis.com/">Anthithesis</a>, a general-purpose simulator testing platform. They have gone as far as developing <a href="https://antithesis.com/blog/deterministic_hypervisor/">a deterministic hypervisor</a>. Another well-known implementer of DST is <a href="https://docs.tigerbeetle.com/about/vopr/">TigerBeetle</a>, a financial database.</p>
<h2 id="in-conclusion">In conclusion</h2>
<p>The presentation was interesting and there was plenty of questions during and after the presentation. Great discussion altogether! I’m looking forward for the next meetup.</p>
<p><strong>Photo:</strong> Overripe berries of lily-of-the-valley in autumn sun.
I was going to take a photo of the presentation for this post but I was following it so closely that I forgot!</p>
Weeknote 12: In the weeds
https://quanttype.net/p/weeknote-12-in-the-weeds/
Mon, 04 Nov 2024 00:00:00 +0000https://quanttype.net/p/weeknote-12-in-the-weeds/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/muovipullot_hu_1589e7082860bdab.webp" type="image/webp" />
<img src="https://quanttype.net/images/muovipullot.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p><a href="https://quanttype.net/posts/2024-10-27-weeknote-11-weeknotes.html">Last time</a> I thought I had a trick in my pocket to ensure that I get some writing done:</p>
<blockquote>
<p>The solution I’m going to try is free writing, similar to morning pages: set a timer for ten minutes and just write whatever is on your mind.</p>
</blockquote>
<p>It did not quite work the way I hoped for.
Free writing is great, but it’s just that my work last week was in the weeds and I cannot write about it in detail.</p>
<hr>
<p>Instead, I’m going to tell you about my Mastodon data exporter.</p>
<p>Before Mastodon had search, I wanted to be able to search <a href="https://mastodon.social/@Miikka/">my own toots</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
I made some scripts to download the data locally and search them in my text editor.</p>
<p>I’m using the <a href="https://simonwillison.net/2020/Oct/9/git-scraping/">“git scraping” technique</a> described by Simon Willison. I made a Python script that gets all my statuses via Mastodon’s API and stores them in a local JSON file. Then I made a private GitHub repo and a GitHub Actions cronjob that runs the script and stores the result in the repo.</p>
<p>Whenever I want to search my posts, I pull the repo.
I’ve got another script that formats the data into a Markdown file and I’ve also imported the data into a SQLite file. The SQLite file is useful if I want to find e.g. <a href="https://mastodon.social/@Miikka/112757676373796883">my most popular post</a>.</p>
<p>I haven’t shared the scripts because I’ve got the scripts and the data all mixed up in one repo.
Maybe one day. In any case, it’s nice that GitHub provides us this free infrastructure you can use to automate tasks like this.</p>
<p><strong>Photo:</strong> Empty plastic water jugs lying in high grass, illuminated by autumn sun.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Maybe it was possible to search your own toots in Mastodon already back then,.I learned about it after I already had built the exporter in any case. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Weeknote 11: Weeknotes
https://quanttype.net/p/weeknote-11-weeknotes/
Sun, 27 Oct 2024 00:00:00 +0000https://quanttype.net/p/weeknote-11-weeknotes/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/syksy4_hu_a654334d3cbdb5ef.webp" type="image/webp" />
<img src="https://quanttype.net/images/syksy4.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>I’ve now done ten weeks of <a href="https://quanttype.net/tags/weeknote.html">weeknotes</a>.
Let’s talk about how it has gone.
In <a href="https://quanttype.net/posts/2024-08-18-weeknote-1-schema-evolution.html">the first note</a>, I wrote this:</p>
<blockquote>
<p>I haven’t posted much. I’d like to change that. Instead of trying harder, I’d like to <a href="https://drmaciver.substack.com/i/75221362/prompts-for-agency">try solving it</a>. A friend suggested posting weeknotes, so here goes.</p>
</blockquote>
<p>Certainly it worked in the sense that I’ve posted every week.
In general I’ve seen three kinds of benefits from blogging:</p>
<ol>
<li>Writing clarifies my thoughts for myself.</li>
<li>Sharing the writing with people I know sparks conversations.</li>
<li>Getting a broad audience for the writing builds my reputation.</li>
</ol>
<p>The weeknotes have hit the first and the second points on the list.
My thoughts have been clarified and I’ve had a bunch of great conversations.</p>
<p>Whether these posts are an asset for my reputation as a software engineer, I don’t know.
I have not attempted to circulate the posts widely as I don’t think they’re likely to get much traction.
I’ve mostly shared them with a few groups of friends and <a href="https://mastodon.social/@Miikka/">on Mastodon</a>.</p>
<p>I’ve spent maybe an hour per week on writing the weeknotes.
It’s not much, so considering the benefits, it has been time well spent.
I’m planning to continue the practice for the time being.</p>
<h2 id="making-it-smoother">Making it smoother</h2>
<p>There are a few things I want to change.</p>
<p>The earlier in the week you start writing the note, the easier it will be to write.
The trouble is that sometimes you notice that it’s already Thursday and you feel you haven’t thought anything worthwhile.</p>
<p>The solution I’m going to try is free writing, similar to <a href="https://quanttype.net/posts/2020-11-29-morning-pages.html">morning pages</a>: set a timer for ten minutes and just write whatever is on your mind.</p>
<p>My work has been a bit scattered and you can see it in how the weeknotes jump from one topic to another.
However, I’ve now been able to focus more tightly on <a href="https://quanttype.net/posts/2024-09-08-weeknote-4-debugging-tools.html">building an internal tool</a> and I’m hoping that I can now bring more continuity to the weeknotes as well.</p>
<p>Finally, I’ll drop the non-engineering tidbits.
They have been well-received but they feel out of place in the weeknotes.</p>
<p><strong>Photo:</strong> A yellow leaf lying in a puddle on asphalt.</p>
Weeknote 10: Prototyping
https://quanttype.net/p/weeknote-10-prototyping/
Mon, 21 Oct 2024 00:00:00 +0000https://quanttype.net/p/weeknote-10-prototyping/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/syksy1_hu_6947cc998667046b.webp" type="image/webp" />
<img src="https://quanttype.net/images/syksy1.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>My week wasn’t too focused as I was slightly ill, so I’m going to offer a selection of small thoughts.</p>
<hr>
<p>My main concern at work has been how to get the tool <a href="https://quanttype.net/posts/2024-09-08-weeknote-4-debugging-tools.html">I started working on a while ago</a> to the users.
The work has been slow due to my <a href="https://quanttype.net/posts/2024-09-25-weeknote-6-heart-of-clojure.html">conference</a> <a href="https://quanttype.net/posts/2024-10-13-weeknote-9-eurorust-2024.html">trips</a> and other priorities, but I’ve gotten to the point
where I <em>must</em> get feedback from the potential users.</p>
<p>I’ve tried to figure out what is needed to make their work easier.
However, I haven’t myself done the work they’re doing, so it’s just a guess.
I can continue to build based on my hunches, but more I build without validation,
more there will be wasted work.</p>
<p>There’s a chicken-and-egg problem, though.
You’ve got to build something to show the users and have them try it out,
but you need to get feedback from the users to know what to build.</p>
<p>Anyway, I found some people willing to try my prototype.
It feels like wheels are spinning forward again.
Remains to see if there’s any traction.</p>
<hr>
<p>I’m using <a href="https://fastapi.tiangolo.com/">FastAPI</a> and the way I develop the app is by using FastAPI’s development server (<code>fastapi dev</code>). Under the hood (I believe) it uses Uvicorn’s auto-reload support to reload the code whenever the code changes. The auto-reloader works by restarting the whole Python process whenever the code changes.</p>
<p>What’s good about this approach is that it gives you a clean slate.
There’s no old code dangling around.
What’s bad is that it’s slow if you have heavy dependencies.</p>
<p>In my case, the deps are heavy indeed and it takes seconds to reload.
If I was using Clojure, I could just send the changed functions to the running server via REPL or use <a href="https://github.com/clojure/tools.namespace">tools.namespace</a> to reload just the changed code.</p>
<p>Is there anything like that for Python? I may try to rig up something.
Waiting for seconds takes too long.</p>
<hr>
<p>Since I was a bit ill and I had to stay at home,
I started playing <em>The Legend of Zelda: Echoes of Wisdom</em>.
It’s the latest game in the series and the first Zelda game I’ve played since <em>Oracle of Seasons</em> on GameBoy Color.</p>
<p><em>Beware: There will be slight spoilers about the game below the photos!</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/syksy3_hu_f7944db0a0fd25f8.webp" type="image/webp" />
<img src="https://quanttype.net/images/syksy3.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/syksy2_hu_98cf65cea70e674.webp" type="image/webp" />
<img src="https://quanttype.net/images/syksy2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<p>I hate the boss fights!</p>
<p>The boss fights are puzzles.
You’ll have to figure out the weird trick that works on this boss and then do it three times or so.
After you defeat the boss for the first time, it comes back with a more powerful attack, as is traditional.
Just like there has to be modulation in every Eurovision song!</p>
<p>The main game mechanic is that you can create “echoes” of items you’ve seen
and monsters you’ve encountered. Usually the solution to the boss fight is to use
one of the monsters.
This means that you’ll run around dodging the boss attacks and drinking potions and trying stuff until you figure out something that works.</p>
<p>Once you figure out the trick, then you run around dodging the attacks and spawning the monsters until the boss is defeated.
It feels like a chore.</p>
<p>Some other echo puzzles are nice, though. I like the jumping puzzles, where you have to get to some hard-to-reach spot by building bridegs, ladders, and platforms. The most important item in the game is the <em>old bed</em>. It’s the basic 2x1x1 tile item and the solution to so many puzzles.</p>
<p>I’ve had good time so far, but I’ve played now about two thirds of the main quest (I think) and it’s starting to feel repetitive to be honest.</p>
<p><strong>Photos:</strong> Autumn vibes from Suvilahti, Helsinki.</p>
Weeknote 9: EuroRust 2024
https://quanttype.net/p/weeknote-9-eurorust-2024/
Sun, 13 Oct 2024 00:00:00 +0000https://quanttype.net/p/weeknote-9-eurorust-2024/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ferris_hu_415341e6ac580a36.webp" type="image/webp" />
<img src="https://quanttype.net/images/ferris.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>This week I attended <a href="https://eurorust.eu/2024/">EuroRust 2024</a> in Vienna, Austria.
It was a two-day conference about the Rust programming language, organized by
<a href="https://mainmatter.com">Mainmatter</a>, a Rust consultancy<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and
this was the second time it was organized.</p>
<p>This was my first Rust conference.
Unfortunately, it ended up being more of a miss than a hit for me, and it might be the last one in a while.</p>
<p>It was on me, really. I go to conferences to meet people, not for the talks.
However, I was not in the mood for getting to know new people and I didn’t have many pre-existing connections to the community, so the hallway track didn’t amount to much.</p>
<h2 id="conference-impressions">Conference impressions</h2>
<p>Overall the conference was well-run. There were some snags, but nothing unusual.
The talks that I attended were fine, but none of them really hit it out of the park.
Not much to report.</p>
<p>There was a bit of unconference in the form of “impl rooms”.
Open source maintainers could post ideas for contributing to their projects and there was time and space reserved for them to mentor the contributors. I did a couple of PRs but unfortunately they could not be merged during the event.</p>
<p>So was there anything surprising about the event?</p>
<p><strong>Level of experience.</strong> I’ve used Rust professionally alongside Python since late 2023
and I’ve thought myself as a newcomer to Rust,
but it turned out I have plenty of experience already.</p>
<p>Many attendees seemed enthusiastic about Rust but they weren’t actually using it much.
They wanted to find Rust jobs or to introduce Rust at the workplace.
As a veteran of Clojure and Haskell communities, I’ve had this conversations many times before!
I think the Rust people will have more success, though.</p>
<p><strong>Focus.</strong> The conference seemed focused on the language itself.
This was foreshadowed by <a href="https://eurorust.eu/2024/talks/through-the-fire-and-the-flames/">the opening talk</a> by <a href="https://thesquareplanet.com/">Jon Gjengset</a> about things people struggle with when learning Rust. He dove into technical details and talked about things like understanding the <code>Send</code> and <code>Sync</code> traits.</p>
<p>I’ve spent my time on nerding over programming languages,
but I’m more interested in what kind of systems people are building in Rust and how.
<a href="https://www.crmarsh.com/">Charlie Marsh</a>’s <a href="https://eurorust.eu/2024/talks/building-an-extremely-fast-python-package-manager-in-rust/">closing talk</a> about how <a href="https://github.com/astral-sh/uv">uv</a>, the Python package manager, was built in Rust and what kind of tricks they used to make it fast was much more to my liking.</p>
<p><strong>Descriptive complexity theory.</strong> From <a href="https://amandastjerna.se/">Amanda Stjerna</a>’s <a href="https://eurorust.eu/2024/talks/the-first-six-years-in-the-development-of-polonius/">talk on Polonius</a>, the next generation of Rust borrow checker, the following factoid stuck to my head: <a href="https://en.wikipedia.org/wiki/Datalog#Complexity">Datalog is a language for programs that can be run in P</a>. Surely I’ve encountered this fact before, but it’s interesting in any case.</p>
<h2 id="vienna-was-cool-though">Vienna was cool though</h2>
<p>After the first conference day, I decided to skip the dinners and went to
St. Stephen’s Cathedral to listen to a concert by the Finnish organist Jan Lehtola.
That was great! Bach’s Chaconne was a powerful experience
when played with the giant organ of the cathedral.</p>
<p>I also had time for Egon Schiele’s paintings
at Leopold Museum and for a glass of Austrian wine at <a href="(https://weinskandal.at/pages/rundbar)">Die Rundbar</a> (great wine, relaxed atmosphere).
Vienna seemed like a nice city and I would like to go there again.</p>
<p><strong>Photo:</strong> The huge inflatable EuroRust mascot at the conference venue.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A programming language conference organized by a consultancy specialized in the language?
I feel like I’ve seen this pattern <a href="https://clojutre.org/">with some other programming language</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Weeknote 8: Feedback
https://quanttype.net/p/weeknote-8-feedback/
Sun, 06 Oct 2024 00:00:00 +0000https://quanttype.net/p/weeknote-8-feedback/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rock_hu_e3571d53e26cd8d2.webp" type="image/webp" />
<img src="https://quanttype.net/images/rock.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>It is the <em>perf</em> season at work.
We’ve been reviewing our own and each other’s job performance.
The reviews will feed into managers’ assessment of our performance which in turns affects whether we will get a raise or a promotion.</p>
<p>I struggled a bit with writing the reviews.
I realized I haven’t been giving as much feedback as I should have been and now I’ve got a backlog and the perf review is an awkward place to bring up new feedback.
Praise is fine, but it’s not fun to get surprise negative feedback during the perf review.
It’d be better to receive it directly from the other person when it’s fresh.</p>
<p>I talked with a few friends about how to get better at giving feedback.
It would be much easier to do if there was a culture of feedback where it was normal and safe to share feedback.
But how to foster such culture?</p>
<p>The best advice I got was to <em>start by soliciting feedback yourself</em>.
It builds the relationship and lays the groundwork for also giving it.</p>
<h2 id="its-also-apple-season">It’s also apple season</h2>
<p>The Finnish word for apple crumble is <em>omenahyve</em>, literally “apple virtue”.
Considering how delicious it is, it might as well be apple vice.</p>
<p><strong>Photo:</strong> A rock I saw in at the Kauhala crag in Kirkkonummi.</p>
Weeknote 7: Memray + k8s
https://quanttype.net/p/weeknote-7-memray-in-kubernetes/
Mon, 30 Sep 2024 00:00:00 +0000https://quanttype.net/p/weeknote-7-memray-in-kubernetes/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/fog_hu_96adf4ff72c951a3.webp" type="image/webp" />
<img src="https://quanttype.net/images/fog.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>I wanted to track a memory leak in a Python program.
The program was leaking only in production and so I had to figure out how to use <a href="https://github.com/bloomberg/memray">Memray</a> to attach to a process in Kubernetes.
There were a few hurdles on the way, so here’s what I did.</p>
<ol>
<li>Add Memray to your container image. We use Poetry for our Python projects, so I added Memray as a dependency.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">poetry add memray
</span></span></code></pre></div></li>
<li>If you’re using <a href="https://hub.docker.com/_/python/">the official Docker Python images</a> as a base, be sure to use the non-slim variant. The debug symbols have been stripped from the slim variant.</li>
<li>Memray relies gdb (or alternatively lldb), so install that. We’ll also need the <code>setcap</code> binary, so install that too. On Debian-based images:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">apt-get install gdb libcap2-bin
</span></span></code></pre></div></li>
<li>Unless you’re running a privileged container, gdb needs <code>CAP_SYS_TRACE</code> capability to work.
As explained in <a href="https://github.com/benfred/py-spy/blob/8dd54929106916a3c961cc57c1172793ce126180/README.md#how-do-i-run-py-spy-in-kubernetes">py-spy docs</a>, add it to <code>Deployment.spec.template.spec.containers</code> in your k8s spec
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">securityContext</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">capabilities</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">add</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">SYS_PTRACE</span><span class="w">
</span></span></span></code></pre></div></li>
<li>Use <a href="https://www.man7.org/linux/man-pages/man8/setcap.8.html">setcap</a> to add <code>CAP_SYS_PTRACE</code> to
the permissible and effective capability sets of the gdb binary.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">setcap cap_sys_ptrace+pe /usr/bin/gdb
</span></span></code></pre></div></li>
<li>Use <code>kubectl exec</code> to attach Memray to your Python process. Typically it’s PID is 1.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">kubectl <span class="nb">exec</span> -it <your pod> -- memray attach <span class="m">1</span>
</span></span></code></pre></div></li>
</ol>
<p>This starts memray’s live TUI. In practice you’ll want to generate a flamegraph, but I’ll let you to figure that out.</p>
<p>Note that <code>CAP_SYS_PTRACE</code> can be used for privilege escalation.</p>
<p>Something you should <em>not</em> do is to use setcap to set <code>cap_sys_ptrace</code> on the Python binary. The trouble is that it makes the actual process you want to inspect non-dumpable. As explained by <a href="https://www.man7.org/linux/man-pages/man2/PR_SET_DUMPABLE.2const.html">PR_SET_DUMPABLE</a> man page, a process’s dumpable attribute can get set to 0 when it executes a program that has capabilities:</p>
<blockquote>
<p>The process executes (execve(2)) a program that has file capabilities (see capabilities(7)), but only if the permitted capabilities gained exceed those already permitted for the process.</p>
</blockquote>
<p>I did this mistake because I thought that Memray itself needs the capability.
That’s not the case since it relies on gdb.</p>
<h2 id="debugging-notes">Debugging notes</h2>
<ul>
<li><code>/proc/PID/status</code> is your friend - see the lines starting with <code>Cap</code> for capability sets. You can use <code>capsh --decode</code> to make sense of the numebrs.</li>
<li>If <code>/proc/PID/mem</code> is owned by root even though the uid of the process is something else, that means the process is non-dumpable</li>
</ul>
<h2 id="recommendation-making-music-with-c64">Recommendation: Making music with C64</h2>
<p>I bumped into <a href="https://linusakesson.net/">Linus Åkesson’s</a> video <a href="https://www.youtube.com/watch?v=ly5BhGOt2vE">Making 8-bit Music From Scratch at the Commodore 64 BASIC Prompt</a> and I recommend watching it. He shows how to code a small sequencer on Commodore 64 and makes some music with it.</p>
<p><strong>Photo:</strong> Fog over reed in a sea shore on an autumn morning. Sunrise colors the clouds pink.</p>
Weeknote 6: Heart of Clojure
https://quanttype.net/p/weeknote-6-heart-of-clojure/
Wed, 25 Sep 2024 00:00:00 +0000https://quanttype.net/p/weeknote-6-heart-of-clojure/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/bleuven_hu_eedf239d9c389733.webp" type="image/webp" />
<img src="https://quanttype.net/images/bleuven.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Last week I attended <a href="https://2024.heartofclojure.eu/">Heart of Clojure</a>.
It was held the last time in 2019 and <a href="https://quanttype.net/posts/2019-08-12-hallway-track-conference.html">I had such great time</a> that I decided to go again even though I don’t use Clojure these days. I knew what to expect, but nevertheless I ended up surprised.</p>
<p>I gave a lightning talk where I told everyone to blog as it clarifies their thoughts and feelings.
Well, here’s a plot twist:
Heart of Clojure ended up being an emotionally complex event for me and I have hard time putting it into words, at least in public.
Therefore I’m just going skip it.</p>
<p>I’ll list a few things I liked about it. The keynote about working in the open by <a href="https://www.todepond.com/">Lu Wilson</a>. Meeting in person people I’ve known for years over the Internet. Meeting old friends. Meeting new people. The livecoding set by <a href="https://pulusound.fi/">pulu</a>. Juggling. Food. Giving a lightning talk (always give a lightning talk!). Belgian chocolate. In his keynote <a href="https://ericnormand.me/">Eric Normand</a> talked about category theory without mentioning the words <em>category theory</em>.</p>
<p>Luckily, other people have written about it and <a href="https://tonitalksdev.com/">Toni Väisänen</a> even made a video. Here are a few links:</p>
<ul>
<li><a href="https://handwritten.blog/2024-09-24-hoc24.html">Heart of Clojure 2024: It’s okay</a> by Daniel Janus</li>
<li><a href="https://manueluberti.eu/posts/2024-09-21-heart-of-clojure/">Scrappy hearts and Clojure fiddles</a> by Manuel Uberti</li>
<li><a href="https://www.leftfold.tech/posts/heart-of-clojure-2024/">Travel Log: Heart of Clojure 2024</a> by Johnny</li>
<li><a href="https://www.youtube.com/watch?v=GEqzw6V31tk&feature=youtu.be">People of Heart of Clojure</a> by Toni Talks Dev</li>
</ul>
<p>Big thanks to everyone involved!</p>
<h2 id="more-conferences">More conferences</h2>
<p>Jamie Brandon has announced <a href="https://www.scattered-thoughts.net/writing/hytradboi-2025">HYTRADBOI 2025</a>.</p>
<p>Back in 2022, he organized a little online conference called <em>Have You Tried Rubbing A Database On It</em>, or <a href="https://www.hytradboi.com/">HYTRADBOI</a>. The conference was <a href="https://www.scattered-thoughts.net/writing/hytradboi-2022-postmortem/#outage">a chaos</a>, but it was also great fun and it had a big impact on me. Have you noticed that databases are everywhere? Have you noticed that <em>databases are cool</em>?</p>
<p>I’m definitely attending it again, and submitting a lightning talk, too, if there’s an opportunity.</p>
<h2 id="even-more-conferences">Even more conferences</h2>
<p><a href="https://eurorust.eu/">EuroRust 2024</a> is right around the corner and I will be attending it. Say hi to me if you’re coming too!</p>
Weeknote 5: Broken Input
https://quanttype.net/p/weeknote-5-broken-input/
Sun, 15 Sep 2024 00:00:00 +0000https://quanttype.net/p/weeknote-5-broken-input/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/honk_hu_28b4e8ef61f65ce3.webp" type="image/webp" />
<img src="https://quanttype.net/images/honk.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I was thinking about debugging tools <a href="https://quanttype.net/posts/2024-09-08-weeknote-4-debugging-tools.html">last week</a>.
The same theme continues this week.</p>
<p>My big insight is that many development and debugging tools must work with <em>broken or incomplete inputs</em>. Here are a few examples:</p>
<ul>
<li>A debugger must work with programs that crash. In fact, that is exactly the time when many of us reach for a debugger.</li>
<li>Your editor should work even if there are some syntax or type errors in your code. You expect features like syntax highlighting and autocompetion to work while you’re in the middle of writing code.</li>
</ul>
<p>This also applies to the tool I’m working on. If you want to use it for debugging, it’s not enough that it works for the kind of well-behaved data that we’ve got in production. It also has to work on the kind of data that we get when things are not ready yet and we’ve got some debugging to do.</p>
<p>Dynamic typing is possibly another example of allowing broken input.
I’m a big fan of Python’s type system, but I’ve enjoyed <em>not</em> using mypy while prototyping new code. It’s convenient that you can have slightly broken code and you can just run it and see how it behaves. When you’re experimenting and playing, you don’t need to handle every <code>None</code> or other corner case.</p>
<h2 id="recommended-read">Recommended read</h2>
<p>You should read the article <a href="https://modem.io/blog/blog-monetization/">How to Monetize a Blog</a>. I guarantee it’s worth your time even if you don’t have a blog or plans to monetize one.</p>
<h2 id="upcoming-events">Upcoming events</h2>
<p>I’m going to be at <a href="https://2024.heartofclojure.eu/">Heart of Clojure</a> the next week. If you’re coming, come say hi to me!</p>
<p><strong>Photo:</strong> Geese in a park in Münich.</p>
Weeknote 4: Debugging Tools
https://quanttype.net/p/weeknote-4-debugging-tools/
Sun, 08 Sep 2024 00:00:00 +0000https://quanttype.net/p/weeknote-4-debugging-tools/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sea_hu_7a16211a9e831128.webp" type="image/webp" />
<img src="https://quanttype.net/images/sea.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>I’m switching the gears a bit at work and instead of working on databases, I’ll be working on a new internal developer tool for making sense of some timeseries data. That is what has been on my mind this week.</p>
<p>Essentially I’m making a debugging tool. What’s the bar for a successful debugging tool?
<em>At least it must be better than printf.</em></p>
<p>There are endless articles where experienced engineers admit that they debug software by inserting <code>printf</code> calls instead of reaching for a debugger like <code>gdb</code>. I do it, too, and printf debugging has a couple of pretty big upsides compared to more powerful tools.</p>
<ul>
<li>You already know how to do it and how it works. Printing is one of the first thing you learn when you start learning programming.</li>
<li>It works almost everywhere.</li>
<li>It fits into your existing workflow – just add a line of code and re-run your code or tests.</li>
<li>The performance penalty is small and you control where you incur it.</li>
</ul>
<p>I’ve tried the Python debugger integration in VS Code a few times and it has not been a success. The instructions for setting it up were cryptic and I wasn’t sure if it’s going to work. It made the whole program very very slow, not just the parts that I wanted to debug. The control flow is difficult to understand when you step through async code. Ultimately I gave up.</p>
<p>It’s tough to beat printf on its own turf, but when it comes to timeseries data, printf has a weakness: it’s difficult to analyze series of long lists of numbers by just printing them out. Having a graph would help <em>a lot</em>.</p>
<p>Now, there’s an abundance of tools for visualizing timeseries data. I hope to make use of some them. Certainly I’m not going to implement my own plotting library. The problem for me to solve is how to integrate them so smoothly in our developers’ workflows that they actually want to use them.</p>
<h2 id="literary-pondering-on-being-aimless">Literary pondering: On being aimless</h2>
<p>I’m reading <em>Pussikaljaromaani</em> by Mikko Rimminen. Published in 2004, it’s a novel that chronicles how three good-for-nothings spend a summer day in the Kallio district of Helsinki.</p>
<p>In the spring I went to see <em>Waiting for Godot</em>, the play by Samuel Beckett. In the play, the two main characters Vladimir and Estragon, well, wait for Godot whom they’re supposed to meet any moment now.</p>
<p>What these two works have in common is that in both, the protagonists are spending time. Nay, they are <em>killing time</em>. They have no aim, no goals, other than to pass time. They are idle.</p>
<p>After seeing Waiting for Godot, I found it difficult to relate to but I couldn’t explain why. Now with Pussikiljaromaani, I understand it better: it is the aimlessness.
But why is it an issue? I don’t know yet and it bothers me a bit. This thought is still in progress; eventually there will be a conclusion.</p>
<p>My recommendation this week, in so far there is any, is to find a cultural work that is widely appreciated and that you don’t get and ponder why.</p>
<p><strong>Photo:</strong> Sea waves are hitting some rocks that are right on the sea level somewhere west of Hanko.</p>
Weeknote 3: Object Storage
https://quanttype.net/p/weeknote-3-object-storage/
Mon, 02 Sep 2024 00:00:00 +0000https://quanttype.net/p/weeknote-3-object-storage/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/bodo_hu_97a93a38d886e35f.webp" type="image/webp" />
<img src="https://quanttype.net/images/bodo.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>This week I’ve been thinking about object storage services such as Amazon S3. Despite being called “object” storage, in my mind they are used for storing files. However, that’s not the only way to think about them. Another perspective is that <a href="https://materializedview.io/p/flink-usage-kv-store-on-s3-terraform-for-data">they are key-value stores</a> for storing binary blobs</p>
<p>Nowadays a lot of people want to use them as a storage backend for their databases because they’re very durable and the data storage is cheap. We do, too, at work.</p>
<p>Retrofitting object storage to an existing system is not easy, though. Richard Artoul from WarpStream explains how difficult it is <a href="https://www.warpstream.com/blog/tiered-storage-wont-fix-kafka">to make Kafka use object storage</a>. Designing a new system based on it is easier but, as <a href="https://materializedview.io/p/cloud-storage-triad-latency-cost-durability">Chris Riccomini explains</a>, the price of API calls remains too high:</p>
<blockquote>
<p>The most naive implementation of a cloud-native LSM might simply send all WAL writes directly to object storage. This works and is reasonably low latency with S3 Express. Unfortunately, it’s expensive when you have a lot of writes. <a href="https://aws.amazon.com/s3/pricing">PUTs are $0.0025 per-1000 requests</a>. A high-volume service that sustains 10,000 writes per-second would cost 2.5c per-second, or $65,000 per-month.</p>
</blockquote>
<p>You need to do some kind of <em>tiered storage</em> where you combine multiple storage mediums with different trade-offs and automatically move data between them. Riccomini’s new project <a href="https://slatedb.io/">SlateDB</a> initially stores the writes in the memory – cheap but not durable – and then flushes them in batches to an object storage service – durable but the API calls are expensive.</p>
<p>There’s another problem. <a href="https://x.com/vanlightly/status/1828717606557106466">Jack Vanlightly points out</a> that latency is a problem. Standard S3 comes with double-digit latency for small objects, and while S3 One Zone Express brings it down to single-digit latency, it comes at the cost of durability.</p>
<p>Object storage makes a great bottom tier for large amounts of rarely-accessed data in a “cloud native” database. But what should the higher layers be?</p>
<hr>
<h2 id="recommendation-lehto--korpi-by-pauli-lyytinen">Recommendation: Lehto / Korpi by Pauli Lyytinen</h2>
<p>This week I’m recommending music again. I just bumped into saxophonist Pauli Lyytinen’s new album <a href="https://wejazzrecords.bandcamp.com/album/lehto-korpi"><em>Lehto / Korpi</em></a> which combines recordings from the Finnish nature with jazz and it’s delightful. I feel like I shouldn’t say too much and you should just listen to the track <a href="https://wejazzrecords.bandcamp.com/track/korpi-iii">Korpi III</a>.</p>
<p><strong>Photo:</strong> The southern tip of Bodö in the Archipelago Sea. I’m not 100% sure if it’s Bodö, but if not, it’s one of the islands around it, maybe Bergholm.</p>
Weeknote 2: Developer Experience
https://quanttype.net/p/weeknote-2-developer-experience/
Sun, 25 Aug 2024 00:00:00 +0000https://quanttype.net/p/weeknote-2-developer-experience/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/cablecar_hu_c116588ddb5a8865.webp" type="image/webp" />
<img src="https://quanttype.net/images/cablecar.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>This week at work we were talking about improving the <em>developer experience</em> around the company. It is about how it feels to work with the technical systems as a software developer. Is it fun or frustrating to develop a new feature? What about debugging a production issue?</p>
<p>If you were truly ambitious about it, you might ask how to make it more fun and more exciting and more meaningful. We were more pragmatic and asked how to make it less frustrating.</p>
<p>Doesn’t lessening frustration make things more fun? To an extent yes, but joy is not just a lack of a frustration. Being less frustrated makes more space for joy but there has to be something else to be joyful about.</p>
<p>But maybe you can’t bring joy or meaning into the work from developer experience perspective. That has to come from the work itself.</p>
<p>Anyway, reducing frustration in development workflow comes down to two things:</p>
<ul>
<li><strong>Reducing friction.</strong> When you want to do something, do you know how to do it? Do you have to ask someone or can you just do it? Do you know how to look things up? Do you need to execute many manual steps? Do you need to make many decisions you don’t care about?</li>
<li><strong>Tightening the feedback loops.</strong> When you do something, you’ll want to know if it had a positive effect. You made a code change – did it work? You deployed something – did it work?</li>
</ul>
<p>These are overlapping themes. You can reduce friction by doing things like improving documentation, automating manual work, and agreeiqng on standard ways of doing things. Feedback loops can be tightened by speeding up build times, test suites, and CI pipelines, and improving observability.</p>
<p>The elephant in the room is <em>technical debt</em>. Legacy systems, hasty implementations, and poor architectural choices are a big source of frustration for software developers, but they cannot be made go away by polishing the workflow.</p>
<p>Every team is going to have tech debt, but you can keep it under control by actively managing it. However, that requires investing time and effort into it and often the decisionmakers aren’t eager to allow that despite it slowing everything down.</p>
<hr>
<h2 id="recommendation-yeung-man-hamburger-helper">Recommendation: Yeung Man Hamburger Helper</h2>
<p>This time I’m going to recommend a recipe I recently discovered.</p>
<p>While we were sailing, a friend cooked us <em>hamburger helper</em> based on <a href="https://www.youtube.com/watch?v=m_AkQaHOoW4">the recipe by Yeung Man Cooking</a>. It’s a pasta dish with tofu, soy sauce, and red wine, inspired by <a href="https://en.wikipedia.org/wiki/Hamburger_Helper">the American food product</a> from the 1970s. I liked it, and I liked it when I cooked it myself at home. There’s no guarantee that what tastes great during a long day on the sea also tastes great at home, but this time it worked.</p>
<p>I’ve been looking for new, easy, vegetarian dishes for my weeknight cooking rotation and after cooking it a few times, looks like it’s going to be one. I don’t cook much with tofu or red wine, so it brings some new variety.</p>
<p>You can read the recipe from the video description but I recommend watching the video. The ASMR style presentation is great and funny.</p>
<p><strong>Photo:</strong> A cable car going to the top of <a href="https://en.wikipedia.org/wiki/Wank_(mountain)">Wank</a>. You can see Garmisch-Partenkirchen in the valley.</p>
Weeknote 1: Schema Evolution
https://quanttype.net/p/weeknote-1-schema-evolution/
Sun, 18 Aug 2024 00:00:00 +0000https://quanttype.net/p/weeknote-1-schema-evolution/
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/mtwank.jpeg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<h2 id="preface-im-trying-out-weeknotes">Preface: I’m trying out weeknotes</h2>
<p>I believe writing regularly in public about your ideas is valuable. Writing helps you to clarify your thinking and sharing it lets you get feedback. You get something you can refer back to and link to.</p>
<p>I’ve been writing regularly. However, as regular readers may have noticed, I haven’t posted much. I’d like to change that. Instead of trying harder, I’d like to <a href="https://drmaciver.substack.com/i/75221362/prompts-for-agency">try solving it</a>. A friend suggested posting weeknotes, so here goes.</p>
<p>Weeknotes are weekly updates about what you’re working on. I’m going to post about a software engineering topic that has been on my mind that week. I can’t write in public in detail about what I work on in my job, but at least I can write about the concepts. I’ll also include some non-engineering tidbit or recommendation.</p>
<h2 id="this-week-schema-evolution">This week: schema evolution</h2>
<p>I’ve been thinking about how data models can be changed in a system where you cannot update all the participants at once. A typical example is a backend service that is called by a mobile app. When you change the backend API schema, the already-in-use versions of the mobile app should continue to work. To make it work, your changes have to be backwards and forwards compatible:</p>
<ul>
<li><strong>Backwards compatible:</strong> data written with an old version of the schema can be read with the new version of the schema.</li>
<li><strong>Forwards compatible:</strong> data written with the new version of schema can be read with old versions of the schema.</li>
</ul>
<p>Backwards compatibility is required so that the backend service accepts requests from the old app versions. Forwards compatibility is required so that the old app versions accept responses from the backend service.</p>
<p>What this exactly means depends on how you have implemented everything. For example, maybe your API schema includes a JSON object that contains an optional field <code>name</code>. Can you remove the field?</p>
<p>From the backwards compatibility perspective, it’s okay if your deserialization code ignores unknown fields. If it doesn’t, you’ll get errors about the unknown field <code>name</code>.</p>
<p>From the forwards compatibility perspective, you need to ask what an optional field means. Does it mean that the field can be omitted entirely or does it mean that the field is nullable, i.e. <code>{"name": null}</code> is acceptable? Do you accept both and do they mean the same thing? If the field can be omitted, then the change is okay.</p>
<p>If you just think about JSON, saying that nullable and optional are the same may sound silly. But if you consider how you’d model an optional field with a data validation library like <a href="https://docs.pydantic.dev/latest/">Pydantic</a> in Python, the sensible way is to use a nullable field:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyModel</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
</span></span></code></pre></div><p>Technically you could omit properties from a Python object instance but that would be strange and un-Pythonic.</p>
<p>You could, of course, avoid the whole problem by building a system that translates the data between schema versions. The most ambitious take in this space is Ink & Switch’s <a href="https://www.inkandswitch.com/cambria/">Cambria</a>. If anyone is running a system like that at scale, I’d love to hear about it.</p>
<hr>
<h2 id="recommendation-ghosts-by-hania-rani">Recommendation: Ghosts by Hania Rani</h2>
<p>A few years back I moved on from Spotify to buying albums and this means that now I listen to the same albums again and again. <a href="https://haniarani.bandcamp.com/album/ghosts"><em>Ghosts</em></a> by Hania Rani is a recent favorite. I like it how her soundscapes are rather abstract, but her singing brings the music back to concrete. It’s a good album to listen to in the morning as the songs have energy but they’re not in your face about it.</p>
<p><strong>Picture:</strong> A view from the top of <a href="https://en.wikipedia.org/wiki/Wank_(mountain)">Wank</a>, a mountain near Garmisch-Partenkirchen.</p>
paketoi 0.1
https://quanttype.net/p/paketoi-0/
Tue, 23 Apr 2024 00:00:00 +0000https://quanttype.net/p/paketoi-0/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lentokone_hu_5d38bf9c79ad9dbc.webp" type="image/webp" />
<img src="https://quanttype.net/images/lentokone.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>A few months ago I wrote about <a href="https://quanttype.net/posts/2024-01-31-creating-aws-lambda-zip-files-with-pex.html">how to build AWS Lambda deployment packages with Pex</a>. It works, but it left me wondering: why isn’t there a one-command solution for building the packages for simple Python projects? I decided to build one.</p>
<p>It’s called <a href="https://codeberg.org/miikka/paketoi"><strong>paketoi</strong></a>. It takes a <code>requirements.txt</code> file and your source files and bundles them into a zip file that you can deploy on AWS Lambda. I’ve released the initial version <a href="https://pypi.org/project/paketoi/">on PyPI</a>.</p>
<p>It’s a bit rough right now - it is version 0.1 after all - but I hope to polish it later on.</p>
<h2 id="why-use-paketoi">Why use paketoi?</h2>
<p>AWS Lambda’s developer guide has instructions for <a href="https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies">building deployment packages with pip</a>. Why use paketoi instead of them? There are two benefits:</p>
<ol>
<li>It’s a single command instead of a bunch of calls to <code>pip</code> and <code>zip</code></li>
<li>It works around <a href="https://github.com/pypa/pip/issues/11664">that pip bug</a> that sometimes results in wrong versions of dependencies being installed, depending on the Python version you’re using.</li>
</ol>
<p>How it works under the hood is that it downloads deps with <a href="https://pypi.org/project/pex/">pex</a> like in <a href="https://quanttype.net/posts/2024-01-31-creating-aws-lambda-zip-files-with-pex.html">my previous post</a>. It comes with the “complete platform information” files, so you don’t have to care about them. As a bonus, the result is zipped with <a href="https://pypi.org/project/repro-zipfile/">repro-zipfile</a>, so the checksum of the deployment package stays the same if the inputs stay the same.</p>
<h2 id="usage">Usage</h2>
<p>For full installation and usage instructions, see <a href="https://codeberg.org/miikka/paketoi">the README</a>.The easiest way to install it is with <a href="https://github.com/pypa/pipx">pipx</a>:
<code>pipx install paketoi</code>.</p>
<p>Here’s a small example. Let’s say you have a simple lambda with just source code file, <code>lambda_function.py</code>, and some dependencies listed in <code>requirements.txt</code>.</p>
<pre tabindex="0"><code>.
├── lambda_function.py
└── requirements.txt
</code></pre><p>Let’s say you want to build a deployment package that works with Python 3.12 runtime on arm64 architecture. You can do that by running the following command in the project directory:</p>
<pre tabindex="0"><code>paketoi -r requirements.txt --runtime 3.12 --platform arm64 lambda.zip
</code></pre><p>Now upload <code>lambda.zip</code> to AWS Lambda and enjoy your function.</p>
<h2 id="could-you-use-uv-instead">Could you use uv instead?</h2>
<p><strong>Update 2024-09-11:</strong> Looks like <a href="https://github.com/astral-sh/uv/releases/tag/0.1.39">uv has added the <code>--target</code> flag</a> since I wrote this post.
So this section is wrong and you could, in fact, use uv.</p>
<p><a href="https://github.com/astral-sh/uv">uv</a> is a brand new Python package installer developed by Astral, the same company that also develops <a href="https://github.com/astral-sh/ruff">ruff</a>. uv is billed as a drop-in replacement for pip so you might ask if it has the same shortcoming as pip.</p>
<p>Unfortunately uv <a href="https://github.com/astral-sh/uv/issues/1517">does not yet support the <code>--target</code> flag</a>, so it cannot be easily used for building the deployment packages.</p>
<h2 id="but-what-about-poetry">But what about Poetry?</h2>
<p>Are you using <a href="https://python-poetry.org/">Poetry</a> instead of <code>requirements.txt</code> like <a href="https://quanttype.net/posts/2023-10-31-do-not-use-requirements.txt.html">I have recommended</a> on this blog? That’s great! I’ve noticed that since my post about Pex, a new Poetry plugin has appeared: <a href="https://github.com/micmurawski/poetry-plugin-lambda-build">poetry-plugin-lambda-build</a>. I have not tried it out, but it looks potentially useful.</p>
A toolbox of methods
https://quanttype.net/p/a-toolbox-of-methods/
Tue, 26 Mar 2024 00:00:00 +0000https://quanttype.net/p/a-toolbox-of-methods/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaata_hu_cac0bf2189f1362c.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaata.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>There’s a lot of debate about software engineering methods and paradigms such as test-driven development (TDD), pair programming, agile methods, functional programming, and so on. Which one is the best?</p>
<p>This is how the debate is often framed: it’s about finding <em>the one true method</em> that will deliver the best results and should be used always. Someone will write a post putting forward their favorite method; another person will counter that it won’t work in their specific situation, thus clearly it isn’t good for anything.</p>
<p>I think this misses the mark. The way I think about methods is that you have <em>a toolbox of methods</em> out of which you pick the right tool for the job.</p>
<p>For example, Hillel Wayne recently wrote about <a href="https://buttondown.email/hillelwayne/archive/what-mob-programming-is-bad-at/">What Mob Programming is Bad At</a>. He posited that pairing and mobbing are great for knowledge sharing but they suck for optimization work.</p>
<p>My experience is the same. I’m glad that I have pairing in my toolbox and I can use it when it works well such as when onboarding people to a new project. I don’t think it’s the best choice for every situation. I have other tools for those times.</p>
<p>Code review is another great tool that I use all the time and advocate for, but let’s face it: <a href="https://quanttype.net/posts/2021-02-06-when-to-not-use-code-review.html">sometimes all it does is slow you down</a>.</p>
<p><a href="https://www.linkedin.com/pulse/three-rules-engineering-plus-one-how-drive-innovation-ray-carnes">Any tool can be used as a hammer</a> but you do not have to.</p>
<hr>
<p>Listing all the variables that affect the suitability of a method is left as an exercise for the reader. Here’s a starter:</p>
<ul>
<li>Team composition and experience level</li>
<li>Interpersonal dynamics and power dynamics</li>
<li>Time and resource constraints</li>
<li>Type of work: design, feature development, maintaining an existing system, debugging, optimization</li>
<li>Goal of the work: prototyping, shipping production-quality code, learning, knowledge sharing</li>
</ul>
GitHub's PRs could be better
https://quanttype.net/p/githubs-prs-could-be-better/
Thu, 29 Feb 2024 00:00:00 +0000https://quanttype.net/p/githubs-prs-could-be-better/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/luistelija_hu_70fabc99fe23520d.webp" type="image/webp" />
<img src="https://quanttype.net/images/luistelija.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>At work, we use GitHub to collaborate on code.
We create short-lived feature branches and merge them back to the main branch via pull requests (PRs).
This is a fairly standard workflow.</p>
<p>Unfortunately I’m not too happy with it.
I’ve had trouble finding a perfect way of working with git and GitHub’s PR view.</p>
<p>I’d like to have the following:</p>
<p><strong>Useful git history.</strong>
There are many opinions on what makes git history great.
Myself, I look at the blame annotations regularly, so for me descriptive PR titles are the most important part.
<code>Implement <feature></code> or <code>Fix <bug></code> are great; <code>Code review fixes</code> or <code>Make it work</code> not so much.
When using command-line tools, you can use <code>--first-parent</code> to see the merge commits instead<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>,
and in VS Code and on GitHub you can quickly jump to the PR that touched the line.</p>
<p><strong>Easy re-reviews.</strong>
As a PR reviewer, it’s nice if you can easily see what has changed since the previous review round.
If the PR reviewer has pushed new commits, then it’s easy: GitHub can show you changes from the commits that were added since the last review.
However, if the author has amended the existing commits and force-pushed them, then GitHub won’t do this.</p>
<p><strong>No busywork or custom tools.</strong>
When I’m ready to merge, I’d like to just press a button in GitHub to do so.</p>
<hr>
<p>So, what’s the issue?</p>
<p>When you make changes to your PR after the first round of review, you need to either add new commits or amend the existing ones.
If the changes are small, then it’d be better to amend them into the existing commits to avoid messy history, but then you won’t get easy re-reviews.</p>
<p>As a compromise, we sometimes create <code>fixup!</code> commits and push those. Once the PR has been approved, we then rebase them with autosquash, force-push, and add the PR to the merge queue.
You cannot just merge by pushing a button on GitHub.
Maybe we should script this, but this goes against my desire to avoid custom tools.</p>
<p>You could also consider using GitHub’s <em>Squash and merge</em> option, which squashes all the commits into a single commit on top of the default branch. This could be a great option if you do single-commit PRs anyway, except for one thing: now git on your computer cannot tell that the branch was merged. <code>git branch -d</code> will complain, <code>git rebase -i</code> will include stray commits, and if you use <a href="https://martinvonz.github.io/jj/">Jujutsu</a>, <code>jj git fetch</code> does not hide the merged branch.</p>
<p>I’m not going to try to tell GitHub what they should do, but the situation does not feel optimal!</p>
<hr>
<p>A general point: when we debate about how to best use git, it’s not just about git itself. It’s also about all the tools that integrate with git: the code forge, the editor, the CI system, etc. It’s also about the people with whom you use git.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I recently learned that GitHub now allows you to <a href="https://github.blog/changelog/2022-08-23-new-options-for-controlling-the-default-commit-message-when-merging-a-pull-request/">include the PR title and description</a> in the merge commit message. This improves the usefulness of seeing the merge commits a lot! <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Creating AWS Lambda zip files with Pex
https://quanttype.net/p/creating-aws-lambda-zip-files-with-pex/
Wed, 31 Jan 2024 00:00:00 +0000https://quanttype.net/p/creating-aws-lambda-zip-files-with-pex/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/oksat_hu_e4fdd3d366fae94.webp" type="image/webp" />
<img src="https://quanttype.net/images/oksat.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p><strong>Update:</strong> See also <a href="https://quanttype.net/posts/2024-04-23-paketoi-0.1.html">my new tool that simplifies this</a>.</p>
<p>So, you want to deploy a Python script to AWS Lambda and you have a few dependencies with native code.
How do you build the <code>.zip</code> deployment package for it?</p>
<p>Let’s say that you have your script in <code>lambda_function.py</code> and your dependencies listed in <code>requirements.txt</code>. If you want to follow along at home, I’ve prepared <a href="https://github.com/miikka/lambda-deployment-package">a GitHub repo</a> with an example script.</p>
<aside><em>Nota bene:</em> Even though I <a href="https://quanttype.net/posts/2023-10-31-do-not-use-requirements.txt.html">recommend against using requirements.txt</a>, that’s what I’m going to do in this post. It is unfortunately the most universal dependency specification format for Python. If you want to use these instructions with Poetry, you can use <a href="https://github.com/python-poetry/poetry-plugin-export">the export plugin</a> to create a requirements.txt file.</aside>
<p><a href="https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies">AWS Lambda’s documentation</a> suggests a pip invocation that looks like this for installing the dependencies in the directory <code>package</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pip install <span class="se">\
</span></span></span><span class="line"><span class="cl"> --platform manylinux2014_x86_64 <span class="se">\
</span></span></span><span class="line"><span class="cl"> --target<span class="o">=</span>package <span class="se">\
</span></span></span><span class="line"><span class="cl"> --implementation cp <span class="se">\
</span></span></span><span class="line"><span class="cl"> --python-version 3.12 <span class="se">\
</span></span></span><span class="line"><span class="cl"> --only-binary<span class="o">=</span>:all: --upgrade <span class="se">\
</span></span></span><span class="line"><span class="cl"> -r requirements.txt
</span></span></code></pre></div><p>You can then create a <code>.zip</code> file like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">cd</span> package
</span></span><span class="line"><span class="cl">zip -r ../package.zip .
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ..
</span></span><span class="line"><span class="cl">zip package.zip lambda_function.py
</span></span></code></pre></div><p>And that’s it: <code>package.zip</code> is your deployment artifact.</p>
<p>However, the <code>pip</code> invocation above does not always result in a correct deployment package due to a <a href="https://github.com/pypa/pip/issues/11664">shortcoming in pip</a>. If your local environment does not match the AWS Lambda platform, the result may be wrong.</p>
<p>Hopefully the issue is fixed some day. Meanwhile, a common solution is to run the same command inside a Docker container. That works, but Docker on macOS is annoyingly slow. Wouldn’t it be great to have a correct solution without Docker?</p>
<p>Turns out <a href="https://pypi.org/project/pex/">pex</a> offers one.</p>
<h2 id="lets-do-it-with-pex">Let’s do it with pex</h2>
<p><a href="https://pypi.org/project/pex/">Pex</a> is a tool for generating <em>Python Executable</em> files. It allows you take a Python program and all its dependencies and wrap them into a single <code>.pex</code> file that can be executed with <code>python</code>. The idea is similar to <a href="https://stackoverflow.com/a/11947093/297366">uberjars</a> that are used to deploy Java and Clojure programs.</p>
<p>Taking a Python program and its dependencies and wrapping them into a single <code>.zip</code> file is what we want to do for AWS Lambda and pex’s new <code>pex3</code> variant can do that. You can provide it with “complete platform information” that allows it to choose the right wheels, unlike pip.</p>
<p>You can install pex with <a href="https://github.com/pypa/pipx">pipx</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pipx install pex
</span></span></code></pre></div><p>Note that this installs two binaries, <code>pex</code> and <code>pex3</code>. They have different features and command-line interfaces. We will use <code>pex3</code>.</p>
<h2 id="getting-the-complete-platform-information">Getting the complete platform information</h2>
<p>You can get the complete platform information for your local environment like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pex3 interpreter inspect --markers --tags
</span></span></code></pre></div><p>The result is a large JSON blob containing environment information such as Python version and the list of <a href="https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/">platform tags</a> compatible with your environment.</p>
<p>However, we do not want platform information for your laptop. Instead, we need it for your AWS Lambda environment. <a href="https://github.com/huonw">Huon Wilson</a> offers <a href="https://github.com/pantsbuild/pants/issues/18195">a solution</a> on the issue tracker of Pants build system: upload the following code to AWS Lambda and run it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">subprocess</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">lambda_handler</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"""
</span></span></span><span class="line"><span class="cl"><span class="s2"> pip install --target=/tmp/subdir pex
</span></span></span><span class="line"><span class="cl"><span class="s2"> PYTHONPATH=/tmp/subdir /tmp/subdir/bin/pex3 interpreter inspect --markers --tags
</span></span></span><span class="line"><span class="cl"><span class="s2"> """</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'statusCode'</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'body'</span><span class="p">:</span> <span class="s2">"</span><span class="si">{}</span><span class="s2">"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></div><p>Grab the result from the logs and store it in a file called <code>complete_platform.json</code>.</p>
<p>It’s crude but effective. I’ve run the code on AWS Lambda for Python 3.12 on x86_64. You can see <a href="https://github.com/miikka/lambda-deployment-package/blob/main/complete_platform.json">the result</a> on GitHub.</p>
<p>I don’t know how often AWS changes their Python environment in such a way that you would need to generate a new file. My guess would be that not very often.</p>
<h2 id="building-the-deployment-package">Building the deployment package</h2>
<p><code>pex3 venv create</code> will build a Lambda-compatible zip file for you if you use <code>--layout flat-zipped</code>. Like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pex3 venv create <span class="se">\
</span></span></span><span class="line"><span class="cl"> --layout flat-zipped <span class="se">\
</span></span></span><span class="line"><span class="cl"> --dir package <span class="se">\
</span></span></span><span class="line"><span class="cl"> --complete-platform complete_platform.json
</span></span><span class="line"><span class="cl"> --no-build <span class="se">\
</span></span></span><span class="line"><span class="cl"> -r requirements.txt
</span></span><span class="line"><span class="cl">zip package.zip lambda_function.py
</span></span></code></pre></div><p>And that’s it! Upload <code>package.zip</code> to AWS Lambda and try it out.</p>
<h2 id="what-if-there-are-no-pre-built-wheels">What if there are no pre-built wheels?</h2>
<p>You might encounter a dependency with native code and no pre-built wheels.
Unfortunately Pex won’t magically set up a cross-compiling environment for you.
Using Docker might really be the easiest solution for ensuring a consistent build environment.</p>
<h2 id="an-exercise-for-the-reader">An exercise for the reader</h2>
<p>Wouldn’t it be cool if there was a simple tool that built AWS Lambda deployment packages quickly and correctly? Pip and pex and Docker get the job done, but it feels complicated.</p>
<p>Considering how popular both Python and AWS Lambda are, I’m surprised that there does not seem to be popular tool that would <em>just do it</em>.</p>
<p><code>poetry bundle lambda</code>, anyone?</p>
Yearnote 2023
https://quanttype.net/p/yearnote-2023/
Mon, 08 Jan 2024 00:00:00 +0000https://quanttype.net/p/yearnote-2023/<p>Hello and happy new year. Like <a href="https://quanttype.net/posts/2023-01-21-yearnote-2022.html">last year</a>, I’m going to indulge in self-reflection and tell you about my year.</p>
<p><em>On the photos: there’s one photo for each month, in chronological order.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-01_hu_361c64bdc09747be.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-01.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-02_hu_a8c62ec6b2e12fe9.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-02.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="software-engineering">Software engineering</h2>
<p>I poured a lot of energy into technical work. I built a system for managing large data migrations and spent a lot of time on polishing and operating a large cloud backend written in Python.</p>
<p>It was nice to spend a lot of time on actually implementing things. I learned a lot about new technology. I now know a lot more about various AWS services (AWS Lambda, looking at you, and DynamoDB too) and about using Python at scale.</p>
<p>The flipside of the coin is that I’m not sure if I’m any more skilled at shipping large projects than I was a year ago. Technical work is in my comfort zone; collaboration and coördination less so.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-03_hu_3ac30dca276861d6.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-03.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-04_hu_d50c28c98859bfc7.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-04.jpg" width="600" height="337.5" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="blogging">Blogging</h2>
<p>I wrote this about 2022:</p>
<blockquote>
<p>I didn’t blog much in the latter half of the year. I was busy at work and I didn’t have much to say. I hope to get back to blogging soon, though. I’ve learned a few lessons worth sharing and it would be great to participate in <em>the software engineering community’s intellectual discourse</em>.</p>
</blockquote>
<p>I didn’t get back to blogging in 2023. A part of the problem is that after leaving the Clojure community behind, I haven’t become a member of another software engineering community. Writing in the void doesn’t work – you have to write for someone and right now I don’t know who I am writing for.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Still, I wrote one good post about Python packaging, titled <a href="https://quanttype.net/posts/2023-10-31-do-not-use-requirements.txt.html">“Do not use requirements.txt”</a>. Thanks to everyone who engaged with it! I didn’t realize it would strike such a nerve, but it got a lot of attention. Alas, the trouble with Python packaging is here to stay.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-05_hu_a7ba8c82474f8bbe.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-05.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-06_hu_83aeb701de8b1f00.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-06.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="eating-better">Eating better</h2>
<p>I chaged how I cook and eat and it improved my life!</p>
<p>In fall, I was having trouble with cooking for myself on weekdays. Starting to cook when you’re already hungry is the worst. After work, I would often procrastinate with cooking and then feel miserable the whole evening.</p>
<p>Luckily I bumped into <a href="https://www.booritney.com/writing/meal-prep">booritney’s ultimate guide to meal prep</a>. Obviously I had heard about meal prep before, but it was such an inspiring post that I started meal prepping immediately.</p>
<p>I have always cooked with the intention of eating the leftovers the next day, but now I reserved time for cooking even bigger batches of food. I started to assemble the dishes into ready-to-eat portions and ensured that I have a small dessert for each meal.</p>
<p>Turns out this works really well for me! I enjoy cooking as long as I don’t have to do it while hungry, so batching it into time when I have the energy for it was a great improvement.</p>
<p>Despite all the meal prep, I was often weirdly hungry and craving for treats. I decided to keep a food diary for a week. My conclusion was that… I’m not eating enough. I started eating more and I became less hungry. Wow!</p>
<p>My calorie and protein intakes were clearly under various recommended numbers. This is not a common problem for the average Finn in this day and age, so it had not occurred to me to consider it. I’m glad it turned out to be so simple to fix. Being hungry sucks.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-07_hu_653944ea08884a8.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-07.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-08_hu_f08e9915313cc1f4.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-08.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="outdoors-life">Outdoors life</h2>
<p>In the summer, I kayaked <em>Soisalo Runt</em>. Located in Lake Saimaa between Varkaus, Kuopio and Heinävesi, <a href="https://en.wikipedia.org/wiki/Soisalo">Soisalo</a> is the largest island in Finland. I paddled around it, starting and ending in Varkaus.</p>
<p>It took me nine days to paddle the 245 km trip. Along the way there are a bunch of beautiful places like Southern Kallavesi and Kolovesi National Park. I especially enjoyed the scenery of Heinävesi Route and it was fun to paddle downstream.</p>
<p>The trip was not without hardships. The constant rain and mosquitos made my mood miserable and my new paddling jacket gave me a rash. Still, I finished the planned route. That was some <a href="https://www.rei.com/blog/climb/fun-scale">Type 2 fun</a>.</p>
<p>As a cherry on the top, I went through Taipale Canal with a kayak. Going through a 160 m lock with five meter difference in the water level was exciting and slightly scary.</p>
<p>I also participated in Nordic Sea Kayak Camp in Inkoo organized by <a href="https://www.merimelonta.fi/etusivu/">Suomen Merimelonta</a> (formerly known as NIL Finland). It was a great weekend of kayaking lessons, hanging out with other kayakers, and even a small competition.</p>
<p>My favorite lesson was the one about paddling backwards, taught by Anssi Nupponen. Turns out it is fairly easy to do as long as you understand how it works, and that understanding also transfers to paddling forwards.</p>
<p>In addition to kayaking, I climbed indoors a lot, sailed a bit, ice skated a bit, and did a small hike in Lapland.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Also, I fell through the ice while walking on a frozen lake. It was less unpleasant than I expected.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-09_hu_89cd7c57869cb858.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-09.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-10_hu_692b9c0979d808f8.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-10.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="reading">Reading</h2>
<p>I read a bunch of books and I did a <a href="https://mastodon.social/@Miikka/109720000428392652">Fedi thread</a> on them. It was a nice exercise to write at least a couple sentences about each book.</p>
<p>The book I’ve thought about the most this year was a graphic novel. Kate Beaton’s <em>Ducks: Two Years in the Oil Sands</em> is a memoir about how she graduates from the university and goes to work at Canada’s oil sands to pay off her student debt. A big topic is being one of few women at the isolated work camps (content warning: sexual abuse). Recommended!</p>
<p>Beaton is also known for her web comic <a href="http://www.harkavagrant.com/">Hark! A Vagrant</a>, which I recommend for lighter mood.</p>
<h2 id="best-of-2023">Best of 2023</h2>
<ul>
<li><strong>Best album</strong>: <a href="https://vegatrails.bandcamp.com/album/tremors-in-the-static">Tremors in the Static</a> by <em>Vega Trails</em>. Lovely atmospheric jazz. In general I love everything released by <a href="https://www.gondwanarecords.com/">Gondwana Records</a>.</li>
<li><strong>Best porridge:</strong> The rice porridge from Helsinki Christmas market with browned butter and miso caramel. I didn’t actually have it at the Christmas market but I followed <a href="https://www.hs.fi/ruoka/art-2000010044438.html">the recipe</a> (in Finnish only) and it turned out great!</li>
</ul>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-11_hu_518d974d78c7805c.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-11.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2023-12_hu_9cc6b94197910a0b.webp" type="image/webp" />
<img src="https://quanttype.net/images/2023-12.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="what-about-2024">What about 2024?</h2>
<p>I don’t know yet. I’ll figure it out.</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p><em>In each yearnote, I express (lack of) surprise at the current cabinet of the goverment of Finland.</em></p>
<p>Last year, I predicted that Sanna Marin’s cabinet would fall apart right before the election like Juha Sipilä’s cabinet did. That did not happen but I will not let it put a damper on my political punditry.</p>
<p>I don’t think Petteri Orpo’s cabinet is going to fall apart in 2024. The cabinet has gone through many scandals already and their politics face strong opposition. Nevertheless, the cabinet parties continue to enjoy their voters support. Prime Minister Orpo gets it that you can stay in power as long as you keep the cabinet parties happy.</p>
<p>I expect that the scandals will continue, though. The Finns Party has a strong track record there and many of their politicians got elected by being provocative and polarizing. I don’t think they are planning to stop.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This post in specific is for my friends, but that’s not the case for my more engineering-focused posts. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>Before writing this post, my perception was that I had slow outdoors year. After going through all my photos and notes, I’d say I actually had a pretty good outdoors year. It’s easy to forget all
that you have done when you hear your friends’ cool stories. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Do not use requirements.txt
https://quanttype.net/p/do-not-use-requirements/
Tue, 31 Oct 2023 00:00:00 +0000https://quanttype.net/p/do-not-use-requirements/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pyhaluosto_hu_9396c51898bf01e3.webp" type="image/webp" />
<img src="https://quanttype.net/images/pyhaluosto.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>Are you developing a backend service in Python? I have two pieces of advice for you:</p>
<ul>
<li>Do not use pip and <code>requirements.txt</code> to manage Python dependencies. They lack crucial features that should be built-in.</li>
<li>Use <a href="https://python-poetry.org/">Poetry</a> instead.</li>
</ul>
<p>To me, the first one is a no-brainer. The second one is more tentative: Poetry is a great option, but it’s hardly the only option worth considering. I’ll explain below.</p>
<aside><em>Nota bene:</em> If you use Python for something else than building backend services, then the advice in this post may not apply to you. For example, if you're <a href="https://gregoryszorc.com/blog/2023/10/30/my-user-experience-porting-off-setup.py/">a library developer migrating off setup.py</a>, it's not obvious that Poetry would be a perfect fit.</aside>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pyhaluosto2_hu_39d7a29b7899cdb3.webp" type="image/webp" />
<img src="https://quanttype.net/images/pyhaluosto2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<h2 id="pips-missing-features">pip’s missing features</h2>
<p><a href="https://pypi.org/project/pip/">pip</a> is a tool that you can use to install packages from <a href="https://pypi.org/">The Python Package Index (PyPI)</a>. It comes with Python and if you’re a Python developer, you have probably used it many times.</p>
<p>The traditional way to manage dependencies for a Python project was to list them in a file called <code>requirements.txt</code> and use <code>pip install -r requirements.txt</code> to install. However, pip was designed to be a package installer and not a full-fledged project workflow tool. <strong>pip lacks two essential features, dependency lockfiles and automatic management of virtualenvs</strong>.</p>
<h2 id="dependency-lockfiles">Dependency lockfiles</h2>
<p>If you want to get same behavier in all environments - your laptop, CI, production - you need to pin the versions of your dependencies and their transitive dependencies. You can pin the versions of your direct dependencies in a <code>requirements.txt</code> by specifying for example <code>requests==2.31.0</code> instead of <code>requests</code>.</p>
<p>However, pip won’t pin the versions of the transitive dependencies. This can be solved by using <a href="https://github.com/jazzband/pip-tools">pip-tools</a> to expand <code>requirements.txt</code> into a file that lists the full dependency graph with exact versions and checksums for the artifacts. pip-tools is great but you need to set up it yourself and figure out how it fits your workflow.</p>
<p>This feature is table stakes in other languages - for example, npm has had <code>package-lock.json</code> for years now and Cargo has <code>Cargo.lock</code>. This really should be a built-in feature in a project workflow tool.</p>
<h2 id="automatic-management-of-virtualenvs">Automatic management of virtualenvs</h2>
<p>The way to create isolated environments in Python is by the use of <a href="https://docs.python.org/3/library/venv.html">virtualenvs</a>. Traditionally you manage them manually: you create one with a shell command (<code>python -m venv example</code> to create a virtualenv called <code>example</code>) and when you want to use it, you need to activate it with another shell command.</p>
<p>This is error-prone: forgetting to activate the virtualenv or activating a wrong virtualenv are common mistakes. There are bunch of workarounds. For example, you can use <a href="https://github.com/pyenv/pyenv-virtualenv">pyenv-virtualenv</a> to make your shell auto-activate a virtualenv when you enter a project directory. direnv <a href="https://github.com/direnv/direnv/wiki/Python">can do it</a>, too.</p>
<p>Again, this too should be a built-in feature in your workflow tool. You should not need to glue multiple tools together. You won’t hear about npm or Cargo users having problems with virtualenvs.</p>
<h2 id="poetry-and-other-options">Poetry and other options</h2>
<p>Fortunately, a lot of people have identified these problem and worked to solve them. Less fortunately, this has resulted in an explosion of Python project workflow tools. So how to pick one?</p>
<p>My recommendation is: <strong>go with <a href="https://python-poetry.org/docs/">Poetry</a></strong>. It has lockfiles, it has virtualenv management, it’s popular and actively developed. In my experience, it’s not perfect but it works.</p>
<p>You could also consider <a href="https://hatch.pypa.io/latest/">Hatch</a> or <a href="https://github.com/pdm-project/pdm">PDM</a>. They’re similar to Poetry. I haven’t used them myself, but I’ve heard other people use them with success. Hatch seems to be especially popular with library authors.</p>
<p>If you’re looking for a more powerful option that can deal with e.g. multiple subprojects, <a href="https://www.pantsbuild.org/">Pants build system</a> has great Python support. It has significantly steeper learning curve however.</p>
<p>Finally, if you’re looking for a rustup-style solution that can install Python for you, there’s <a href="https://github.com/mitsuhiko/rye">rye</a>. It’s new and experimental, but maybe it’s the right choice for you?</p>
<h2 id="where-is-the-canonical-workflow-tool">Where is the canonical workflow tool?</h2>
<p>It would be great if Python came with a canonical project workflow tool. A lot of people wish that pip would become one. Node.js comes with npm and Rust comes with Cargo, so why can’t Python come with one? Why are there so many competing options?</p>
<p>The biggest obstacle, to my knowledge, is that since Python is used so widely and for so many different use cases, coming up with a universal official solution is difficult and slow (and underfunded) work. It’s not clear if pip is the right home for these features, either.</p>
<p>If you want to learn more, read and listen to these people who are, unlike me, deeply involved in the Python community:</p>
<ul>
<li>Stargirl (Thea Flowers) on Fediverse: <a href="https://hachyderm.io/@stargirl/109697057391904145">So You Want to Solve Python Packaging: A Practical Guide</a></li>
<li>Pradyun Gedam: <a href="https://pradyunsg.me/blog/2023/01/21/thoughts-on-python-packaging/">Thoughts on the Python packaging ecosystem</a></li>
<li>Talk Python to Me (podcast): <a href="https://talkpython.fm/episodes/show/406/reimagining-pythons-packaging-workflows">Reimagining Python’s Packaging Workflows</a></li>
</ul>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pyhaluosto3_hu_a4701c7e116f1cc.webp" type="image/webp" />
<img src="https://quanttype.net/images/pyhaluosto3.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<h2 id="an-aside-on-clojure">An aside on Clojure</h2>
<p>Clojurists reading my blog may ask: <em>hey, what about Clojure, how come we do not have lockfiles?</em> That’s a great question!</p>
<p>The Clojure community has solved this by always using explicit versions instead of version ranges for dependencies, even in libraries. The version descriptors would actually support ranges, but nobody ever uses them. This way, as long as the version resolution algorithm is stable, you always get the same versions.</p>
<p>In theory, the transitive dependency version mismatches could be a problem, but Clojure is amenable to a coding style where it rarely causes issues.</p>
<p>In contrast, in Python and Node.js communities it is expected that libraries list version ranges for their dependencies and the package management tools complain about version mismatches.</p>
Recipes for updating poetry.lock
https://quanttype.net/p/poetry-recipes/
Thu, 25 May 2023 00:00:00 +0000https://quanttype.net/p/poetry-recipes/<aside><em>Nota bene:</em> This is a notebook post. I'm jotting down
some notes, often based on an ongoing project I'm working on.
There will be less context and explanation than in my normal posts.
</aside>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kuohu_hu_1cbe305033d11c21.webp" type="image/webp" />
<img src="https://quanttype.net/images/kuohu.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>I’ve been using <a href="https://python-poetry.org/">Poetry</a> for package management in Python projects for a while
now and, for what it’s worth, it’s working well for me. However, some regular
tasks require multiple commands with specific arguments. Here are a few recipes
you might find handy.</p>
<p><strong>Updating the lock file after editing pyproject.toml</strong> After you edit
<code>pyproject.toml</code>, you’ll want to update your lockfile and your virtualenv.
Here are the right commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">poetry lock --no-update
</span></span><span class="line"><span class="cl">poetry install --sync
</span></span></code></pre></div><p>Without <code>--no-update</code>, Poetry will upgrade all dependencies that are not pinned
down, which usually is not what you want. Without <code>--sync</code>, Poetry does not remove packages that you have removed from <code>pyproject.toml</code>.</p>
<p>I use these commands so often that I’ve put them into a script called <code>poetry-locksync</code>.</p>
<p><strong>Upgrading a secondary dependency.</strong> If you want to update a direct dependency,
you can edit <code>pyproject.toml</code> and run <code>poetry lock --no-update</code>. But how do you
upgrade a dependency of one of your direct dependencies to a specific version?
You might want to do that to upgrade a package with a security vulnerability,
for example.</p>
<p>One way to do it is by adding the dependency as a direct dependency with <code>poetry add</code> and then removing it again.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">poetry add --lock your-library@latest
</span></span><span class="line"><span class="cl">poetry remove your-library
</span></span></code></pre></div><p><strong>Resolving merge conflicts in the lockfile.</strong> If two developers change the
dependencies at the same time, you will end up with a merge conflict in
<code>poetry.lock</code> at least in the <code>content-hash</code> line. The easiest way to resolve
them is to regenerate the file with Poetry. First, resolve any conflicts in
<code>pyproject.toml</code>. Then you can run this script which I call
<code>git-resolve-poetry-lock</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">git checkout --ours poetry.lock
</span></span><span class="line"><span class="cl">poetry lock --no-update
</span></span><span class="line"><span class="cl">git add poetry.lock
</span></span></code></pre></div>Branchless git workflows
https://quanttype.net/p/branchless-workflows/
Mon, 15 May 2023 00:00:00 +0000https://quanttype.net/p/branchless-workflows/<aside><em>Nota bene:</em> This is a notebook post. I'm jotting down
some notes, often based on an ongoing project I'm working on.
There will be less context and explanation than in my normal posts.
</aside>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/vihrea_hu_a8b05d8dfc0535bb.webp" type="image/webp" />
<img src="https://quanttype.net/images/vihrea.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>I’ve been experimenting with the so-called <em>branchless</em> version-control
workflows. The idea is that instead of using named branches, you just juggle a
bunch of commits on top of the main branch.</p>
<p>Here are a couple of tools implementing the idea - while Sapling and Jujutsu bill themselves as new VCSs, they both work with git repos:</p>
<ul>
<li><a href="https://github.com/arxanas/git-branchless">git-branchless</a>, which builds on top of git</li>
<li><a href="https://sapling-scm.com">Sapling</a> (sl), a new VCS published by Meta</li>
<li><a href="https://github.com/martinvonz/jj">Jujutsu</a> (jj), a new VCS which has some Google backing</li>
</ul>
<p>git-branchless is the one I’ve used most so far. The others I have only dabbled
with.</p>
<p>A central feature of these tools is the “smartlog” which shows the graph of
commits you have on top of the upstream main branch. Sapling’s smartlog output looks like
this - you have three commits on top of main and the <code>@</code> sign indicates that you’ve checked out the commit with hash <code>335bb92d2</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ sl
</span></span><span class="line"><span class="cl">@ 335bb92d2 <span class="m">3</span> seconds ago miikka.koskinen
</span></span><span class="line"><span class="cl">│ fix: fix bug B
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">│ o d5c08952e <span class="m">21</span> seconds ago miikka.koskinen
</span></span><span class="line"><span class="cl">├─╯ feat: implement feature A
</span></span><span class="line"><span class="cl">│
</span></span><span class="line"><span class="cl">o 5ca31cccb <span class="m">69</span> seconds ago miikka.koskinen
</span></span><span class="line"><span class="cl"> refactor: refactor the code base
</span></span></code></pre></div><p>This kind of situation happens to me regularly: I’ve made a refactoring (or a bug
fix) in one branch and I want to start another branch on top of it. But what
happens if you spot a mistake in the refactoring and want to edit in?</p>
<p>In plain git, you’d probably do a <code>fixup!</code> commit on top of either of your
branches - the one with the feature A or the bug fix B - and <code>git rebase -i</code> it
into the refactoring commit. Then you’d rebase the other branch on top of the
new refactoring commit.</p>
<p>In Sapling, you’d switch to the refactoring commit with <code>sl previous</code> or <code>sl goto</code> and use <code>sl amend</code> to edit it. This automatically rebases the descendant
commits on top of the new commit.</p>
<p>This is especially nice if you like to use <a href="https://benjamincongdon.me/blog/2022/07/17/In-Praise-of-Stacked-PRs/">stacked PRs</a>. I’ve lately used them
a lot since I’ve worked on big changes that would be difficult to review at
once. I’ve yet to try any of the tools’ GitHub integration - I’ve just manually
managed the PRs - but the tools make it easier to deal with the code review fixes to the root
PR.</p>
<p>Another thing it’s good for is <a href="https://github.com/arxanas/git-branchless/wiki/Workflow:-divergent-development">creating experimental
commits</a> - having the smartlog and not having to name your branches
removes a lot of friction from branching out for experiments.</p>
<p>All of this is possible with plain git, but the new tools make it more
convenient and less error-prone.</p>
Yearnote 2022
https://quanttype.net/p/yearnote-2022/
Sat, 21 Jan 2023 00:00:00 +0000https://quanttype.net/p/yearnote-2022/<p>Year 2023 is here. So, how was 2022? Allow me to review my year.</p>
<p><em>On the photos: there’s one photo for each month, in chronological order.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-01_hu_68ca259855bc0c11.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-01.jpg" width="600" height="400.5" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-02_hu_46418c304e627580.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-02.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="professionally">Professionally</h2>
<p><a href="https://quanttype.net/posts/2022-01-16-yearnote-2021.html">In January 2022</a> I wrote this:</p>
<blockquote>
<p>For many years, I’ve focused on building web services in Clojure. However, I
feel that I’ve done enough of it for now and more interesting problems and
bigger impact await elsewhere. Thus I’d like to turn a new page in my career.
I didn’t quite manage to do it in 2021, but there were a few starts.</p>
</blockquote>
<p>The new page got turned. In April, I left my job of six years at Metosin (the
Clojure consultancy). After a month and a half of job search, I landed at Oura
Health (the smart ring company) where I work on data storage and access.</p>
<p>Here’s what I learned about job search:</p>
<ul>
<li>It’s a lot of work! Leaving my previous job without having the next one lined
up was the right choice for me so I was able to properly focus on the search
and the interviews. I’d recommend it <em>if</em> you’re in a position financially
and otherwise to do so.</li>
<li>Cold applications are waste of time. I did get the new job by sending an
application, but most of my other applications didn’t even get a response.
Networking on Twitter worked much better and uncovered many interesting
opportunities.</li>
</ul>
<p>The start at the new job was rocky. It’s not so easy to get to know your
coworkers when you are a remote worker joining during the summer vacation season
and I was certainly second-guessing my choice of employer when the company
announced <a href="https://yle.fi/a/3-12520566">layoffs</a> a week after I started. Once the summer was over, I was bit by
a tick, got infected with <a href="https://en.wikipedia.org/wiki/Tick-borne_encephalitis">TBE</a> and ended up on a long sick leave.</p>
<p>Luckily I recovered well. I got back to work and started to finally get up to
speed. I got to know my teammates and people beyond my team and learned
or re-learned the tech that we’re using (Python, various AWS services). I even
got a nice data migration project under my belt.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-03_hu_80df4966dac1a8f3.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-03.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-04_hu_45423d5151780878.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-04.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="blogging-and-microblogging">Blogging and microblogging</h2>
<p>While I was looking for work, I wrote a few posts about <a href="https://quanttype.net/tags/databases.html">databases</a>. I thought
they turned out well:</p>
<ul>
<li><a href="https://quanttype.net/posts/2022-05-02-schema-migrations.html">Schema migrations and avoiding downtime</a></li>
<li><a href="https://quanttype.net/posts/2022-05-16-tasks-of-schema-migration-tool.html">Tasks of a schema migration tool</a></li>
<li><a href="https://quanttype.net/posts/2022-06-20-sql-clojure-editor-support.html">SQL, Clojure, and editor support</a></li>
</ul>
<p>I didn’t blog much in the latter half of the year. I was busy at work and I
didn’t have much to say. I hope to get back to blogging soon, though. I’ve
learned a few lessons worth sharing and it would be great to participate
in <em>the software engineering community’s intellectual discourse</em>.</p>
<p>After Elon Musk took over Twitter, I lost my interest in posting there. I don’t
think any single platform can fully replace Twitter, but there’s a nice thing
going on right now with Fediverse. I migrated there and you can follow me
at <a href="https://mastodon.social/@Miikka">@[email protected]</a>.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-05_hu_43829d6d7ae556fa.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-05.jpg" width="600" height="400.5" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-06_hu_53f43f908a9bb2c5.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-06.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="hobbies">Hobbies</h2>
<p>I first tried bouldering in late 2021. In 2022, it really became my thing. I
spent a lot of time at the climbing gym, doing at least one session almost every
week, often more. I ventured out a few times, too.</p>
<p>What I like about bouldering is the feeling of progress and mastery it gives
you. I’ve surprised myself by how strong I’ve become (not very strong, but I
wasn’t very strong to begin with) and it feels great to be able to climb a
difficult route after enough practice.</p>
<p>I did a few kayaking trips, visited Amsterdam, sailed, read <a href="https://miikka.me/reading-list/2022/">a bunch of
books</a> and completed a <a href="https://miikka.me/reading-list/discworld/">Discworld Pareto Read</a>.</p>
<p>Something I didn’t do was photography. I’m a bit bummed about it! I wish I had
taken more photos of things I’ve done and friends I’ve spent time with. In 2023,
I want to take more photos.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-07_hu_89a6d403c41f0a5d.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-07.jpg" width="600" height="400.5" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-08_hu_24b020aebb50df96.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-08.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="best-of-2022">Best of 2022</h2>
<p>Just some good things that have stuck with me.</p>
<ul>
<li><strong>Best mämmi experience:</strong> I made
<a href="https://en.wikipedia.org/wiki/M%C3%A4mmi">mämmi</a> myself and it turned out
well! Still, not worth the effort, I’ll just buy it the next time.</li>
<li><strong>Best wine bar experience:</strong> Let Me Wine’s pop-up wine bar at Harju8. They
had created a smoky atmosphere by using a fog machine. It was so cool and the
wine was great, but it was so hard to breathe in there that we had to leave
after having only one glass.</li>
<li><strong>Best taco experience:</strong> Bacalar in Amsterdam-Noord. The tacos with meat were great
but the vegetarion options were even better. And they served me my favorite
drink, a mezcal negroni!</li>
<li><strong>Best reading experience:</strong> <em>Radalla</em> by Iida Sofia Hirvonen. Hirvonen’s
command of language is something special.</li>
<li><strong>Best music experience:</strong> <a href="https://giladhekselman.bandcamp.com/album/far-star"><em>Far Star</em></a> by Gilad Hekselman. I love
this kind of rich yet simple jazz.</li>
</ul>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-09_hu_e9962340b35637b5.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-09.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-10_hu_9fed47a0d76c3509.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-10.jpg" width="600" height="450.5" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="what-about-2023">What about 2023?</h2>
<p>I’ve enjoyed my work a lot lately and I hope that continues and I can grow my
impact and influence inside the company. It would be great to find ways to write
and talk about my work externally, too.</p>
<p>I’m going to continue bouldering a lot. I didn’t have time for a proper hike in
2022 and that’s something I would like to rectify in 2023. Or maybe I’ll do a
big kayaking trip.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-11_hu_3ec9944bb4ea0f6.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-11.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2022-12_hu_d950ff4611c5965e.webp" type="image/webp" />
<img src="https://quanttype.net/images/2022-12.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p><em>In each yearnote, I express (lack of) surprise at the current cabinet of the goverment of Finland.</em></p>
<p>Sanna Marin’s cabinet held together like I expected, although there were a few
close calls. <a href="https://yle.fi/a/3-12630457">The patient safety act debacle</a> was
one, and so was the time when the MPs of Center Party (a cabinet member) <a href="https://yle.fi/a/74-20006585">voted
against the nature conservation act</a> proposed by the cabinet.</p>
<p>However, I don’t think that they will hold together until the next parliamentary
election in April. Instead, I think the cabinet will fall apart right before
the election in such a way that it does not really affect any policymaking but may score the parties some points for the election.</p>
SQL, Clojure, and editor support
https://quanttype.net/p/sql-clojure-editor-support/
Mon, 20 Jun 2022 00:00:00 +0000https://quanttype.net/p/sql-clojure-editor-support/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/skorvan1_hu_185c1bd2acb67176.webp" type="image/webp" />
<img src="https://quanttype.net/images/skorvan1.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>When you’re writing code with a modern editor or an IDE, you can count on having
a number of convenient features such as syntax highlighting, autocomplete, and
code navigation. Unless you’re writing SQL, of course!</p>
<p>A lot of editors and IDEs, such as IntelliJ IDEA, have really nice support for
SQL. However, it’s not so easy to benefit from it in practice. If you’re writing
Clojure and you want to use SQL to query a database, you have a few options:</p>
<ol>
<li>Embed SQL into strings in your Clojure code.</li>
<li>Put SQL into <code>.sql</code> files and import them with <a href="https://www.hugsql.org/">HugSQL</a> or similar.</li>
<li>Use a low-level query builder such as <a href="https://github.com/seancorfield/honeysql">HoneySQL</a>.</li>
<li>Use a high-level query builder or an ORM such as <a href="https://github.com/metabase/toucan">Toucan</a>.</li>
</ol>
<p>Let’s look into each of them in more detail.</p>
<hr>
<p><strong>Embed SQL into strings.</strong> A basic query would look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">jdbc/query</span> <span class="nv">db</span> <span class="p">[</span><span class="s">"SELECT title, rating FROM movies WHERE movie_id = ?"</span> <span class="nv">movie-id</span><span class="p">]))</span>
</span></span></code></pre></div><p>This is simple, but most likely you aren’t getting syntax highlighting or other
features for the string. For example, IntelliJ IDEA does support SQL inside Java
strings, but <a href="https://github.com/cursive-ide/cursive/issues/196">it does not work in Cursive</a>.
Furthermore, if you’d need to parametrize query in a way that is not allowed by
the <code>?</code> placeholder, you’ll have to resort to string templating, which is prone
to SQL injection bugs.</p>
<p><strong>Put SQL into <code>.sql</code> files.</strong> With HugSQL, the SQL file would look something
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- :name get-movie-by-id
</span></span></span><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">title</span><span class="p">,</span><span class="w"> </span><span class="n">rating</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">movies</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">movie_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">:</span><span class="n">id</span><span class="w">
</span></span></span></code></pre></div><p>When you load this with HugSQL, it defines a function called <code>get-movie-by-id</code>
which executes the query.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">hugsql/def-db-fns</span> <span class="s">"my_queries.sql"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">get-movie-by-id</span> <span class="nv">db</span> <span class="p">{</span><span class="ss">:id</span> <span class="nv">movie-id</span><span class="p">})</span>
</span></span></code></pre></div><p>What is great about this approach is that now you get the full editor support
for SQL. In Cursive, however, you can’t jump from Clojure code that refers to
<code>get-movie-by-id</code> to its definition in SQL. Jumping to definition works with
normal Clojure functions, but Cursive does not know how to deal with functions
defined by HugSQL.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>HugSQL has advanced support for parametrizing your queries using snippets and
Clojure expressions. If you use Clojure expressions, though, you now have the
opposite problem: there’s Clojure code embedded into your SQL comments and
there’s no editor support for it.</p>
<p><strong>Use a low-level query builder.</strong> With <a href="https://github.com/seancorfield/honeysql">HoneySQL</a>, the query would be built
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">->></span> <span class="p">(</span><span class="nf">sql/format</span> <span class="p">{</span><span class="ss">:select</span> <span class="p">[</span><span class="ss">:title</span> <span class="ss">:rating</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:from</span> <span class="p">[</span><span class="ss">:movies</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:where</span> <span class="p">[</span><span class="ss">:=</span> <span class="ss">:movies.movie_id</span> <span class="nv">movie-id</span><span class="p">]})</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">jdbc/execute</span> <span class="nv">db</span><span class="p">))</span>
</span></span></code></pre></div><p>There’s a lot to like about this. Your editor’s Clojure support works with this.
You won’t get autocomplete for database identifiers, but completion for commonly
used keywords can be good enough. Queries are Clojure data, so you can use the full power of Clojure to generate them.</p>
<p>The main problem is that you need to learn a new, non-canonical syntax for your
SQL queries. You know how to write the query you want in SQL, but now you need
to figure out how to map it to HoneySQL. It shouldn’t be too hard, but over the
years me and my colleagues have struggled with it.</p>
<p><strong>Use a high-level query builder or an ORM</strong>. With Toucan, the query would look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">defmodel</span> <span class="nv">Movie</span> <span class="ss">:movies</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">db/select</span> <span class="nv">Movie</span> <span class="ss">:movie-id</span> <span class="nv">movie-id</span><span class="p">)</span>
</span></span></code></pre></div><p>From the editor support perspective, this is about the same as using HoneySQL.
It does save you quite a bit of boilerplate. Toucan relies on HoneySQL for
advanced paremetrization, so the syntax problem remains.</p>
<hr>
<p>None of the approaches seems like an obvious winner. In practice, every big
project I’ve seen has used a mix of them.</p>
<p>Contrast this with Datomic and MongoDB: the query languages of both can be
represented cleanly enough as Clojure data and so that’s what you use. This
assortment of options does not exist for them because it’s not needed.</p>
<p>In his article <em><a href="https://www.scattered-thoughts.net/writing/against-sql/">Against SQL</a></em>, Jamie Brandon argues that SQL’s drawbacks cause
“a massive drag on quality and innovation in runtime and tooling”. He does not
mention editor support, but I can’t help but think that it’s an example of the
effects of that drag.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I <em>think</em> jumping to definition works in CIDER and/or Calva, but have not verified it. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
What does `identical?` do?
https://quanttype.net/p/what-does-identical-do/
Wed, 15 Jun 2022 00:00:00 +0000https://quanttype.net/p/what-does-identical-do/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/skorvan7_hu_7ff3a84a16e44462.webp" type="image/webp" />
<img src="https://quanttype.net/images/skorvan7.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Dear fellow Clojure enthusiasts, do you know what the following two code
snippets evaluate to? And <em>why</em> is that the result?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">identical? </span><span class="o">##</span><span class="nv">NaN</span> <span class="o">##</span><span class="nv">NaN</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">x</span> <span class="o">##</span><span class="nv">NaN</span><span class="p">]</span> <span class="p">(</span><span class="nb">identical? </span><span class="nv">x</span> <span class="nv">x</span><span class="p">))</span>
</span></span></code></pre></div><p>I didn’t know it a couple of days ago and it took me a while to understand it.
Now I want to share my understanding.</p>
<p>Go on, make a guess and check it with a REPL. When you’re ready – or if you
already saw me post about it <a href="https://twitter.com/arcatan">on Twitter</a> – scroll past the photos below for an
explanation.</p>
<p><em>The photos in this post are from Lill-Skorvan island near Porkkalanniemi, Finland.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/skorvan5_hu_aca594fdc034d717.webp" type="image/webp" />
<img src="https://quanttype.net/images/skorvan5.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/skorvan3_hu_5a75fc87cbbcd3b3.webp" type="image/webp" />
<img src="https://quanttype.net/images/skorvan3.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/skorvan6_hu_8e708801176b62fe.webp" type="image/webp" />
<img src="https://quanttype.net/images/skorvan6.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<p>Here are the results on Clojure 1.11.1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">identical? </span><span class="o">##</span><span class="nv">NaN</span> <span class="o">##</span><span class="nv">NaN</span><span class="p">)</span> <span class="c1">; => true</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">x</span> <span class="o">##</span><span class="nv">NaN</span><span class="p">]</span> <span class="p">(</span><span class="nb">identical? </span><span class="nv">x</span> <span class="nv">x</span><span class="p">))</span> <span class="c1">; => false</span>
</span></span></code></pre></div><p>At first I thought that this is the <code>NaN != NaN</code> feature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> of IEEE 754 floats, but
that is not the case.</p>
<p><code>clojure.core/identical?</code> checks the <em>reference</em> equality of its arguments.
Its <a href="https://clojuredocs.org/clojure.core/identical_q">docstring</a> says:</p>
<blockquote>
<p>Tests if 2 arguments are the same object</p>
</blockquote>
<p><code>##NaN</code> refers to the constant <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Double.html#NaN"><code>Double/NaN</code></a>, which is a primitive double. That is, it’s not
an object. When a primitive value is passed to a Clojure function as an
argument, it gets wrapped into an object. This is called <a href="https://en.wikipedia.org/wiki/Object_type_(object-oriented_programming)#Boxing">boxing</a>. Concretely this means calling
<a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Double.html#valueOf(double)"><code>Double/valueOf</code></a>, which converts the primitive <code>double</code> into a
<code>java.lang.Double</code> object.</p>
<p>The two snippets evaluate to different values because in the first snippet
<code>##NaN</code> gets boxed only once, but in the second snippet each function argument
is boxed separately. This comes down to implementation details of Clojure.
You can see the behavior in the disassembled byte code I posted on <a href="https://ask.clojure.org/index.php/11963/identical-treat-bound-nan-differently-direct-reference">Ask Clojure</a>.</p>
<p>When the reference equality of two boxed doubles is compared, they’re considered
not equal even if they wrap the same value. This explains the results we saw.</p>
<p>Here’s a bonus exercise: what does this evaluate to and why?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">identical? </span><span class="mf">1.0</span> <span class="mf">1.0</span><span class="p">)</span>
</span></span></code></pre></div><!-- Evaluates to false -->
<hr>
<p>I’ve used Clojure for a decade and there are still nooks and crannies I’m not familiar with.
I guess it just takes a while to learn a programming language properly.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://stackoverflow.com/a/1573715">There are good reasons for the feature</a>, or at least they were good back in the day. A lot of programmers dislike floats, but my hot take is that they’re actually successful solution to a complicated problem. What we should do is to start using <a href="https://www.crockford.com/dec64.html">decimal floats</a>, which would match programmer intuitions better than binary floats. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Tasks of a schema migration tool
https://quanttype.net/p/tasks-of-schema-migration-tool/
Mon, 16 May 2022 00:00:00 +0000https://quanttype.net/p/tasks-of-schema-migration-tool/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/cairn_hu_390c98a55656b32f.webp" type="image/webp" />
<img src="https://quanttype.net/images/cairn.jpg" width="600" height="404" loading="lazy" />
</picture>
</figure>
<p>The last time I wrote about <a href="https://quanttype.net/posts/2022-05-02-schema-migrations.html">how database schema migration tools could do more
to help us</a>. The way I see it, any schema migration solution has to cover
three tasks: creating, managing, and executing migrations.</p>
<p><strong>Creating migrations.</strong> The first task is to create a migration script. If
your scripts consist of DDL commands in SQL files, you’ll probably write them by hand.
A linter like <a href="https://github.com/sbdchd/squawk">Squawk</a> can help you to ensure the migrations do not cause unnecessary downtime.</p>
<p>Things get more interesting if you can describe your target schema in another
programming language or as data. For example, when using <a href="https://docs.djangoproject.com/en/4.0/topics/db/models/">Django’s ORM</a> or
<a href="https://github.com/abogoyavlensky/automigrate">automigrate</a> for Clojure, the tool generates the migration automatically
by comparing the target schema to the current schema. Sometimes you may need
to edit the generated migration by hand, but mostly the tool does the job for
you.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><strong>Managing migrations.</strong> You need to keep track of which migrations have been
applied to each of your databases (local, staging, production). When you want to migrate a database, the tool is able
to give you a list of required migrations and the order in which they should be
performed. The tools tackling this problem end up having essentially same
features.</p>
<p>One problem that migration managers need to solve is how to support multiple
developers creating migrations at once. If you number your migrations
incrementally like <a href="https://flywaydb.org/">Flyway</a> does, migrations in concurrenty-developed
branches may end up using the same number.</p>
<p>To avoid this, some tools such as <a href="https://github.com/amacneil/dbmate">dbmate</a> and <a href="https://github.com/yogthos/migratus">Migratus</a> use timestamps in migration names.
Ben Kuhn’s <a href="https://github.com/benkuhn/migrator">sketch of a migration tool</a> has an alternative solution: it
uses incrementing numbers, but it provides a <a href="https://github.com/benkuhn/migrator#version-control"><code>rebase</code></a> command to
automatically renumber the migrations based on Git history. That’s a nice touch!</p>
<p><strong>Executing migrations.</strong> At the basic level this means running the DDL commands
from your SQL files. However, as we learned the last time, you need to be
careful to avoid downtime and do things like <a href="https://squawkhq.com/docs/safe_migrations">set <code>lock_timeout</code> in
PostgreSQL</a>.</p>
<p>While every tool that manages migrations can also execute them, there are
specialist tools such as <a href="https://github.com/github/gh-ost">gh-ost</a> for MySQL which focus only on executing
migrations in a robust way without touching any of the other tasks.</p>
<p>Again, <em>not</em> using DDL for migrations seems to open the door for innovation.
For example, <a href="https://github.com/fabianlindfors/reshape">Reshape</a> for PostgreSQL has a novel way of executing
migrations: by using schemas, views, and triggers, it allows you to use both the
old and the new schema at the same time. This means that your application does
not have to be backwards-and-forwards compatible with database changes.</p>
<p>As seen, there are plenty of tools out there for dealing with these tasks.
There’s no one-size-fits-all solution. If your needs are simple, a simple tool
will suffice. However, if you’re looking to ensure safety and efficiency, you
should look further.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You <em>could</em> describe the target schema with a <code>CREATE TABLE</code> command and have a tool generate the migration by comparing that against the current schema. I’m not aware of such tool, however. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Schema migrations and avoiding downtime
https://quanttype.net/p/schema-migrations/
Mon, 02 May 2022 00:00:00 +0000https://quanttype.net/p/schema-migrations/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaat_hu_b82d294a6d31f44f.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaat.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>If you’re developing an application that is backed by a SQL database, sooner or
later you will need to do a schema migration. Maybe you’ll need to add a new
table or a new column, create a new index, or change some constraint.</p>
<p>Luckily there are plenty of tools to help you! <a href="https://github.com/yogthos/migratus">Migratus</a> for Clojure, <a href="https://flywaydb.org/">Flyway</a>
for Java, and <a href="https://github.com/amacneil/dbmate">dbmate</a> as a more language-independent option are examples of
the most common design pattern: you write migrations as DDL commands in SQL
files and the tool keeps track of which of the migrations have been applied to
the database.</p>
<p>Let’s say you forgot to make the e-mail field of your user table unique and now
you want to fix it. With these tools, the migration could look something like
this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">users</span><span class="w"> </span><span class="k">ADD</span><span class="w"> </span><span class="k">CONSTRAINT</span><span class="w"> </span><span class="n">users_email_unique</span><span class="w"> </span><span class="k">UNIQUE</span><span class="w"> </span><span class="p">(</span><span class="n">email</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>Many object-relational mappers such as <a href="https://docs.djangoproject.com/en/4.0/topics/db/models/">Django’s ORM</a> and <a href="https://guides.rubyonrails.org/active_record_migrations.html">ActiveRecord</a> from
Ruby on Rails exhibit another pattern: the migrations are written using a DSL in
the ORM’s programming language (Python and Ruby, respectively). Here’s what the
migration above could look if it was created with Django:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">migrations</span><span class="p">,</span> <span class="n">models</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Migration</span><span class="p">(</span><span class="n">migrations</span><span class="o">.</span><span class="n">Migration</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">operations</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="n">migrations</span><span class="o">.</span><span class="n">AlterField</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">model_name</span><span class="o">=</span><span class="s1">'user'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">name</span><span class="o">=</span><span class="s1">'email'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">field</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">TextField</span><span class="p">(</span><span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span>
</span></span></code></pre></div><p>One tricky thing about migrations is that to alter a table, you have to lock it
potentially for a long time. This means that migrations can cause downtime.
Braintree has published <a href="https://medium.com/paypal-tech/postgresql-at-scale-database-schema-changes-without-downtime-20d3749ed680">a great guide</a> for avoiding downtime with
PostgreSQL migrations but even then <a href="https://gocardless.com/blog/zero-downtime-postgres-migrations-the-hard-parts/">you have to be careful</a>.</p>
<p>For example, when you add a uniqueness constraint in PostgreSQL, an index gets
created. The guide advises that the migration above should be done in two steps
to avoid grabbing an exclusive lock while the index is being created:</p>
<ol>
<li>First, create a unique index using <code>CREATE INDEX CONCURRENTLY</code>.</li>
<li>Then, add the constraint with <code>USING INDEX</code> which only requires a short-lived exclusive lock to alter the table’s metadata.</li>
</ol>
<p>If you’re like me, you might think that <em>it’d be a great idea to encode all this
knowledge into a tool</em>. And it turns out that people have done it!</p>
<p>There are linters such as <a href="https://squawkhq.com/">Squawk</a> that check your SQL files. Here’s what Squawk
has to say about our migration:</p>
<pre tabindex="0"><code>example.sql:1:0: warning: disallowed-unique-constraint
1 | ALTER TABLE users ADD CONSTRAINT users_email_unique UNIQUE (email);
note: Adding a UNIQUE constraint requires an ACCESS EXCLUSIVE lock which blocks reads.
help: Create an index CONCURRENTLY and create the constraint using the index.
</code></pre><p>Seems good – this is exactly what I was looking for.</p>
<p>Since Django migrations are specified on a higher level, could it do this kind
of tricks automatically? Possibly, but as far as I know, currently it doesn’t.
For ActiveRecord, there’s <a href="https://github.com/braintree/pg_ha_migrations">pg_ha_migrations</a> which implements these ideas.</p>
<p>Tools like <a href="https://github.com/github/gh-ost">gh-ost</a> and
<a href="https://www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html">pt-online-schema-change</a> for MySQL are another stab at this problem. They’re
only concerned with running <code>ALTER TABLE</code> statements in a safe, robust way.
The problem of managing migrations is left to you.</p>
<p>My point is that be careful with your migrations but my meta-point is this:
<em>migrations of heavily-loaded databases take skill and your tools can help you</em>.
I’ve heard it said that all you need for migrations is a bunch of SQL files in a
directory. It goes a long way, yeah, but you can go further.</p>
Clojure and what could've been
https://quanttype.net/p/clojure-and-what-couldve-been/
Wed, 06 Apr 2022 00:00:00 +0000https://quanttype.net/p/clojure-and-what-couldve-been/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/puurijarvi2_hu_1b570d0e23f7aae3.webp" type="image/webp" />
<img src="https://quanttype.net/images/puurijarvi2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p><em>Epistemic status:</em> Anecdotes and opinions.</p>
<p>I’ve used Clojure for over a decade now: I first learned it in 2012 and started
using it professionally in 2013. I’ve been reflecting on what has happened and
what the future looks like. In this post, I want to share a few musings about it.</p>
<h2 id="missed-opportunity-production-repls">Missed opportunity: production REPLs</h2>
<p>REPL is so central to Clojure that people even talk about <a href="https://practical.li/clojure-staging/repl-driven-devlopment.html">REPL-driven
development</a>. It’s not just about having a prompt for typing commands
like in Python<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Instead, it’s a way of connecting your editor to a
running Clojure system.</p>
<p>Usually this is done only during the development. You <em>could</em> connect to the
REPL of production system to debug or maintain it, if you dare. There are
stories about adventurous Clojure developers doing to hotfix a running
system.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Usually these stories end with a word of caution: the devs will
tell you about a time when they forgot to commit the fix to the version control
system and the changes were lost the next time the system was restarted.</p>
<p>You used to be able to do it, anyway. Nowadays when the standard model of deploying
software is ephemeral containers in a Kubernetes cluster, the production REPL is
less meaningful. If you need to apply the same hotfix to eight different
containers and Kubernetes can re-schedule them at any moment, there isn’t much
point to this. Your services are now <a href="http://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/">cattle, not pets</a>.</p>
<p>At Metosin, we had this belief that <em>a small but skilled team using sharp tools
can deliver better software faster</em> than an ordinary team relying on standard
tools. The recent blog post by Tailscale <a href="https://tailscale.com/blog/database-for-2022/">about their database choices</a>
seems like an example of this: SQLite is an unusual choice for their setup, but
they know what they’re doing and it’s working great.</p>
<p>I wonder if we’ve missed a similar opportunity here. Ephemeral containers have their
benefits, especially for scaling, but do they always outweight the productivity
benefits of using production REPLs for debugging?<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<h2 id="faas-did-not-kill-clojure">FaaS did not kill Clojure</h2>
<p>A few years ago, Function-as-a-Service (FaaS) platforms such as AWS Lambda were
surging in popularity. I thought it could be the end of Clojure for web
services. Replacing all the glue code of web services with an API gateway and
some lambdas seemed like a big win: all your code is about the
business logic; the infrastructure has been abstracted away. I didn’t see what
Clojure could offer here: it’s startup time was slow and REPL-driven development
didn’t fit the FaaS platforms.</p>
<p>I was wrong. People still develop long-running server software just as they
did in 2015. AWS Lambda became a popular tool scripting AWS services and for
connecting them to each other, but it didn’t replace traditional web backends.
As far as I understand, the startup time problems have been mitigated, too, and
Clojure lambdas are a feasible choice nowadays.</p>
<p>Thinking back, I expected AWS Lambda developer experience to be rapidly
improved. This didn’t happen and it still clunky if you have a lot of lambdas.
Frameworks such as <a href="https://www.serverless.com/">Serverless</a> tried to smooth it out, but they never hit the
big time. Is this another missed opportunity?</p>
<h2 id="javascript-is-taking-over-clojurescript">JavaScript is taking over ClojureScript</h2>
<p>There’s a lot to like about developing browser applications with ClojureScript.
Tools such as shadow-cljs and Google Closure Compiler are great – I’ll take
them any day over configuring webpack. There are a bunch of good libraries such
as Reagent, and of course the language itself is nice.</p>
<p>Despite the rough spots such as JavaScript interop and awkward testing tools,
for a long time ClojureScript was a clear winner over JavaScript for me.
Today, I’m not so sure about it. JavaScript language, tooling, and ecosystem
have improved immensely over the last decade.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>In some ways, the JavaScript experience is superior now. One essential feature
is async/await syntax for asynchronous programming. It’s much smoother than
using core.async or promises and looks like <a href="https://observablehq.com/@shaunlebron/proposal-generators-and-async-functions-in-clojurescript">we’re not going to have it in
ClojureScript</a>.</p>
<p>Another one is TypeScript. JavaScript ecosystem is strongly embracing TypeScript
and it’s enabling powerful static analysis. I’m not seeing an easy way for
ClojureScript to benefit from that.</p>
<p>JavaScript has improved so much that it’s harder for me to look over
ClojureScript’s rough spots now. I certainly feel that JavaScript is overtaking
ClojureScript. Time will tell if I’m wrong once again.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Python REPL leaves a lot to be desired if you’re used to Clojure
REPLs, but to give it credit, Python has popularized another innovative way of interacting with a
live system: <a href="https://jupyter.org/">notebooks</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>As a teenager, I fixed my websites by editing the PHP files directely on
the server over SFTP. Now that was continuous delivery. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>Another thing I wonder about is what Erlang and Elixir are doing here.
Their support for distributed systems is on the next level. If you have seen a
good article on how Erlang and Elixir people deploy and interact with their
production systems, please send it my way! <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p>Admitedly you may still need to use webpack, and it’s still difficult to configure. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Yearnote 2021
https://quanttype.net/p/yearnote-2021/
Sun, 16 Jan 2022 00:00:00 +0000https://quanttype.net/p/yearnote-2021/<p>2021 is over. It was the second full year of COVID-19, possibly establishing the
new normal.</p>
<p><a href="https://quanttype.net/posts/2021-01-06-yearnote-2020.html">I felt pretty good about 2020</a>.
This year was more of mixed bag. I missed my friends a lot and
felt stuck professionally, but I had a plenty of good time, too.</p>
<p><em>On the photos: there’s one photo for each month, in chronological order.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-01_hu_d24daaa10a74ae90.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-01.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-02_hu_c3423d7c90b631c7.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-02.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-03_hu_2e28724c33cda44e.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-03.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="professional-life">Professional life</h2>
<p>For many years, I’ve focused on building web services in Clojure.
However, I feel that I’ve done enough of it for now and more interesting problems and bigger impact await elsewhere.
Thus I’d like to turn a new page in my career.
I didn’t quite manage to do it in 2021, but there were a few starts.</p>
<p>A higlight was taking a part in a project to
implement a cookie banner for a popular Finnish web service. The banner itself
isn’t that interesting, but I enjoyed learning about the compliance
issues involved. Getting a large organization to honor the users’ data
collection consent is a lot of work even when everyone is on board with the
change!</p>
<p>This made me interested in privacy engineering. However, I didn’t have a chance
to dig into that more deeply. The direction I ended up taking was to become more
involved in the operations: responding to incidents, increasing observability,
learning about resilience engineering.
I’ve always enjoyed debugging so incident response fits me well.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-04_hu_d346ca613e1b6207.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-04.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-05_hu_10b7f17dadc7ce44.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-05.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-06_hu_cde3d0d8eca86c1f.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-06.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="open-source-and-writing">Open source and writing</h2>
<p>It was a slow open-source year for me, but I had some small successes.</p>
<p>In early 2021, I ran a bunch of open source mob programming sessions at Metosin.
Mob programming is like pair programming but with a bigger group of people.
We would fix bugs or implement features over a video call.
One person would share their screen and everybody else would tell them what to do.</p>
<p>This was an experiment to get others more involved in the open source work.
The sessions were well-received but they didn’t become a regular habit.
They needed more preparation than what I had time for.</p>
<p>In April, I thought that I’ll stop the weekly posting cadence on the blog and
post better-thought-out posts more rarely. Well, that didn’t happen.
I did write a lot, but it was all <a href="https://quanttype.net/posts/2021-02-27-smart-notes.html">notes</a> and
<a href="https://quanttype.net/posts/2020-11-29-morning-pages.html">morning pages</a> and none of it ended up on the blog.</p>
<p>Out of the posts I published, here are the ones I liked the best, in chronological order:</p>
<ul>
<li><a href="https://quanttype.net/posts/2021-03-06-clojure-spec-and-untrusted-input.html">clojure.spec and untrusted input</a></li>
<li><a href="https://quanttype.net/posts/2021-03-13-clojure-xml-and-untrusted-input.html">clojure.xml and untrusted input</a></li>
<li><a href="https://quanttype.net/posts/2021-09-04-split-tokens.html">Split tokens in Clojure</a></li>
</ul>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-07_hu_712641d92310a929.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-07.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-08_hu_199f3538c3df31d0.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-08.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-09_hu_86e9cd94d9cf5a7c.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-09.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="hobbies">Hobbies</h2>
<p>It was a big year for my hobbies. Here are some highlights:</p>
<p><strong>Getting a drysuit.</strong> I bought a drysuit (Bora by Palm Equipment) and this
allowed me start the kayaking season early, right after the sea ice was gone. I
did a lot of day paddles between April and July.</p>
<p><strong>Whitewater kayaking course.</strong>
I attended a beginner course organized by my kayaking club, Merimelojat.
We spent a weekend at Pernoonkoski in Kotka.
I learned a lot and it was a lot of fun!
The whitewater community in Finland is not big but seems like they’re having good time.</p>
<p><strong>Hiking.</strong> I did a number of trips but the highlight was my big trip to
Lapland. First I hiked the Hetta-Pallas trail and then did a loop in Urho
Kekkonen national park. Along with <a href="https://quanttype.net/posts/2019-10-24-karhunkierros.html">Karhunkierros</a>,
these are the most classic hikes in Finland.</p>
<p><strong>Bouldering.</strong>
I had tried indoor bouldering a few times during the pandemic and enjoyed it.
In the fall, I gave it a serious go and ended up doing something like 10 sessions over six weeks.
Again, a lot of fun: bouldering combines elements of exercise and puzzle-solving.
Indoor bouldering is also a nice social sport in that it’s easy to try out and
you can have a good time even if your skill levels vary. Hopefully in 2022,
I’ll hit the outdoor boulders as well.</p>
<p><strong>Slay the Spire.</strong>
It’s a deck-builder video game and I played a lot of it.
If you get into the game, I recommend watching <a href="https://www.youtube.com/c/Jorbs">Jorbs’s videos</a> – he’s a great player and has useful explanations for why he does things.
To allow yourself to discover things, play a bit before watching, though.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-10_hu_9202559b553d1fe3.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-10.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-11_hu_aba0d857193c01ab.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-11.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/2021-12_hu_42131344de57a165.webp" type="image/webp" />
<img src="https://quanttype.net/images/2021-12.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</p>
<h2 id="what-about-2022">What about 2022?</h2>
<p>I don’t know. I want to do so many things that there’s no way I can do them all.
I don’t expect the pandemic situation to significantly change during 2022.</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p>I’m not surprised that Sanna Marin’s cabinet held together this year.
There has been some signs of internal conflict, but I still expect them to hold together until the next parliamentary election in 2023.</p>
Split tokens in Clojure
https://quanttype.net/p/split-tokens/
Sat, 04 Sep 2021 00:00:00 +0000https://quanttype.net/p/split-tokens/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ranta2_hu_3bfa7df3c3e768f2.webp" type="image/webp" />
<img src="https://quanttype.net/images/ranta2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>On <em>Dhole Moments</em>, there’s a nice post about a recent <a href="https://soatok.blog/2021/08/20/lobste-rs-password-reset-vulnerability/">Lobste.rs password reset
vulnerability</a>. Via the post, I learned about a simple technique called
<a href="https://paragonie.com/blog/2017/02/split-tokens-token-based-authentication-protocols-without-side-channels">split tokens</a> for making your password reset token validation more resistant to
timing attacks. I wanted to poke at it a bit and ended up creating a tiny
Clojure library for generating and validating split tokens, called <a href="https://github.com/miikka/split-token">split-token</a>.
Check it out if you’re into generating random tokens!</p>
Enjoying the silence
https://quanttype.net/p/enjoying-the-silence/
Tue, 30 Mar 2021 00:00:00 +0000https://quanttype.net/p/enjoying-the-silence/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kahvi_hu_34ed0a30ae8a32d0.webp" type="image/webp" />
<img src="https://quanttype.net/images/kahvi.jpg" width="600" height="400" alt="A coffee dripper on top of a Nalgene bottle on a bench outdoors." loading="lazy" />
<figcaption>I brewed some coffee while on the go.</figcaption>
</picture>
</figure>
<p>Last year, Finland closed down the week I had my winter vacation.
This year, the government was debating <a href="https://yle.fi/uutiset/osasto/news/government_debates_curfews_restrictions/11852176">movement restrictions</a>.
Since COVID-19 broke out in Finland, I’ve thought so many times that “surely this will be over by date X” just to see the date X to come and go.
I’m not going to speculate about the unprecedented restrictions we’re going to see on my winter vacation the next year.</p>
<p>I <a href="https://twitter.com/arcatan/status/1375723939838709763">spent a night at Liesjärvi</a>.
My mom said that it must have been nice to enjoy the silence in the nature.
To which I say: I don’t know about that.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/liesjarvi_hu_c939540663a9f37.webp" type="image/webp" />
<img src="https://quanttype.net/images/liesjarvi.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I was camping next to a lake and I was alone, except for the (quiet) mouse that
wanted to inspect my backpack to see if there’s anything edible.</p>
<p>But still, somebody was camping on the other
side of the lake and they chopped firewood. During the night, there were cranes calling. In the
morning chickadees were singing and during the day woodpeckers pecked the wood.
The lake was frozen and the ice was creaking, booming, and banging.</p>
<p>So much for the silence.</p>
clojure.xml and untrusted input
https://quanttype.net/p/clojure-xml-and-untrusted-input/
Sat, 13 Mar 2021 00:00:00 +0000https://quanttype.net/p/clojure-xml-and-untrusted-input/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kaisla_hu_6a58bc84c9472fb9.webp" type="image/webp" />
<img src="https://quanttype.net/images/kaisla.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Clojure’s standard library includes the namespace <a href="https://clojure.github.io/clojure/clojure.xml-api.html">clojure.xml</a>, which implements a XML parser.
It’s not used much – which is great, because it’s vulnerable to <a href="https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing">XML external entity (XXE) attacks</a>.
It’s something that you want to be aware of if you’re using clojure.xml to process untrusted input.</p>
<p><strong>Update (2022-03-27):</strong> XXE processing has been disabled in <a href="https://github.com/clojure/clojure/blob/master/changes.md#11-security">Clojure 1.11.0</a>.</p>
<p>Juha Jokimäki <a href="https://twitter.com/jjokimaki/status/457545940531150848?lang=en">tweeted about this</a> already back in 2014.
However, I still see clojure.xml occassionally used, so I thought it’s a good idea to blog about it.</p>
<p><em>Note: clojure.xml is not to be confused with <a href="https://github.com/clojure/data.xml">data.xml</a>, which is a separate library.
data.xml has disabled XXE by default.</em></p>
<h2 id="xml-external-entity-attacks">XML external entity attacks</h2>
<p>XML external entities allow you to refer to resources outside of the file that you’re processing.
For example, you can include the content of an external file. Here’s an example from <a href="https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing">OWASP</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp"><?xml version="1.0" encoding="UTF-8" ?></span>
</span></span><span class="line"><span class="cl"><span class="cp"><!DOCTYPE foo [
</span></span></span><span class="line"><span class="cl"><span class="cp"> <!ELEMENT foo ANY ></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY xxe SYSTEM "file:///etc/hostname" ></span>]>
</span></span><span class="line"><span class="cl"><span class="nt"><foo></span><span class="ni">&xxe;</span><span class="nt"></foo></span>
</span></span></code></pre></div><p>Let’s try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; I saved the example above as "hostname.xml"</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="ss">'clojure.xml</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.java.io</span> <span class="ss">:as</span> <span class="nv">io</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">with-open </span><span class="p">[</span><span class="nv">input</span> <span class="p">(</span><span class="nf">io/input-stream</span> <span class="s">"hostname.xml"</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.xml/parse</span> <span class="nv">input</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => {:tag :foo, :attrs nil, :content ["nixos\n"]}</span>
</span></span></code></pre></div><p>My laptop’s hostname is <code>nixos</code>, so that checks out!</p>
<p>If you point the <code>file:///</code> reference to a directory instead of a file, you get a listing of the directory contents.
In principle, you could use <code>http://</code> URLs too, but that did not work on my machine.</p>
<p>If you use a domain name in the <code>file://</code> URL, Java tries to connect to it over FTP.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp"><?xml version="1.0" encoding="UTF-8" ?></span>
</span></span><span class="line"><span class="cl"><span class="cp"><!DOCTYPE foo [
</span></span></span><span class="line"><span class="cl"><span class="cp"> <!ELEMENT foo ANY ></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY xxe SYSTEM "file://quanttype.net" ></span>]>
</span></span><span class="line"><span class="cl"><span class="nt"><foo></span><span class="ni">&xxe;</span><span class="nt"></foo></span>
</span></span></code></pre></div><p>You might able to <a href="https://portswigger.net/web-security/xxe/blind">exfiltrate data</a> using this mechanism.
At least it’s a way to call home and if your FTP server is suitably broken,
the parser seems to get stuck forever.</p>
<h2 id="xml-bombs">XML bombs</h2>
<p>Juha Jokimäki’s <a href="https://gist.github.com/jokimaki/11087906">example code</a> also demonstrates a small <a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">XML bomb</a>.
An XML bomb is a short XML file gets expanded to a extremely large one when processed.</p>
<p>Luckily JDK defines some limits on the entity expansion to hinder this attack.
<a href="https://en.wikipedia.org/wiki/Billion_laughs_attack">The Wikipedia article</a> has an example with a billion-time expansion,
but JDK limits the expansion factor to 64 000 by default.</p>
<p>Thus, the Wikipedia example does not work, but here’s a 1.4 KB file gets expanded to 47 megabytes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp"><?xml version="1.0"?></span>
</span></span><span class="line"><span class="cl"><span class="cp"><!DOCTYPE lolz [
</span></span></span><span class="line"><span class="cl"><span class="cp"> <!ELEMENT lolz (#PCDATA)></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol0 "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol1 "&lol0;&lol0;&lol0;&lol0;&lol0;"></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"></span>
</span></span><span class="line"><span class="cl"> <span class="cp"><!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"></span>
</span></span><span class="line"><span class="cl">]>
</span></span><span class="line"><span class="cl"><span class="nt"><lolz></span><span class="ni">&lol5;</span><span class="nt"></lolz></span>
</span></span></code></pre></div><p>Let’s try it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; Save the example above as "lol.xml"</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">with-open </span><span class="p">[</span><span class="nv">input</span> <span class="p">(</span><span class="nf">io/input-stream</span> <span class="s">"lol.xml"</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">-> </span><span class="p">(</span><span class="nf">clojure.xml/parse</span> <span class="nv">input</span><span class="p">)</span> <span class="p">(</span><span class="ss">:content</span><span class="p">)</span> <span class="p">(</span><span class="nf">first</span><span class="p">)</span> <span class="p">(</span><span class="nf">count</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => 50000000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">/ </span><span class="mi">50000000</span> <span class="mf">1024.0</span> <span class="mf">1024.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => 47.6837158203125</span>
</span></span></code></pre></div><p>It’s not catastrophic: a single XML document won’t crash your server.
Still, you might want to think about it if you process XML files from untrusted sources.</p>
<h2 id="workaround">Workaround</h2>
<p>Juha Jokimäki shows how to create a parser that disallows the document type declarations (DTDs) required by the attacks above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">startparse-sax-no-doctype</span> <span class="p">[</span><span class="nv">s</span> <span class="nv">ch</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">..</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">doto </span><span class="p">(</span><span class="nf">javax.xml.parsers.SAXParserFactory/newInstance</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.setFeature</span> <span class="nv">javax.xml.XMLConstants/FEATURE_SECURE_PROCESSING</span> <span class="nv">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.setFeature</span> <span class="s">"http://apache.org/xml/features/disallow-doctype-decl"</span> <span class="nv">true</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">newSAXParser</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">parse </span><span class="nv">s</span> <span class="nv">ch</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">with-open </span><span class="p">[</span><span class="nv">input</span> <span class="p">(</span><span class="nf">io/input-stream</span> <span class="s">"hostname.xml"</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.xml/parse</span> <span class="nv">input</span> <span class="nv">startparse-sax-no-doctype</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Execution error (SAXParseException) at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper/createSAXParseException (ErrorHandlerWrapper.java:204).</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.</span>
</span></span></code></pre></div><p>However, my recommendation is to replace clojure.xml with <a href="https://github.com/clojure/data.xml">data.xml</a>. It has a couple of benefits:</p>
<ul>
<li>It has nice, full-feature interface.</li>
<li>The parse tree it produces is similar to the one produced by clojure.xml, so for many users it’s a drop-in replacement.</li>
<li>It’s part of the <a href="https://clojure.org/dev/contrib_libs">Clojure contrib</a> library suite, so it’s widely used and maintained.</li>
</ul>
<p>XXE processing is disabled by default:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; clj -Sdeps '{:deps {org.clojure/data.xml {:mvn/version "0.2.0-alpha6"}}}'</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="ss">'clojure.data.xml</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">with-open </span><span class="p">[</span><span class="nv">input</span> <span class="p">(</span><span class="nf">io/input-stream</span> <span class="s">"hostname.xml"</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.data.xml/parse</span> <span class="nv">input</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => #xml/element{:tag :foo}</span>
</span></span></code></pre></div><p>XML bombs are subject to the same limits as clojure.xml, since both the libraries use JDK’s XML parsing facilities.
If you want to prevent them altogether, you can disable DTDs by setting <code>:support-dtd false</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">with-open </span><span class="p">[</span><span class="nv">input</span> <span class="p">(</span><span class="nf">io/input-stream</span> <span class="s">"lol.xml"</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.data.xml/parse</span> <span class="nv">input</span> <span class="ss">:support-dtd</span> <span class="nv">false</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Error printing return value (XMLStreamException) at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl/next (XMLStreamReaderImpl.java:652).</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; ParseError at [row,col]:[11,13]</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Message: The entity "lol5" was referenced, but not declared.</span>
</span></span></code></pre></div><p><strong>Update:</strong> As a follow-up, see <a href="https://clojure.atlassian.net/browse/CLJ-2611">CLJ-2611</a> which aims to disable XXE processing in clojure.xml.</p>
clojure.spec and untrusted input
https://quanttype.net/p/clojure-spec-and-untrusted-input/
Sat, 06 Mar 2021 00:00:00 +0000https://quanttype.net/p/clojure-spec-and-untrusted-input/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/icemachine_hu_1bfc2d4bdc89290c.webp" type="image/webp" />
<img src="https://quanttype.net/images/icemachine.jpg" width="600" height="400.5" loading="lazy" />
</picture>
</figure>
<p>If you’re going to use clojure.spec to validate or conform untrusted input, you should be careful.
It’s easy to write code that looks correct, but opens the door for denial-of-service (DoS) attacks.
For example, if you have implemented a HTTP API in Clojure and you use spec to check the incoming requests,
you should be aware of this.</p>
<p>I believe that this is well-known among the experienced practitioners.
For example, Dominic Monroe recently mentioned the issue in the <a href="https://soundcloud.com/defn-771544745/66-dominic-monroe">defn podcast</a> recently (the section starts around 12:30).
However, I have not seen blog posts about this before.</p>
<p>In clojure.spec, specs for entity maps are <em>open</em>.
This means that they are allowed to have keys that are not included in the spec.
For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.spec.alpha</span> <span class="ss">:as</span> <span class="nv">s</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Let's define a spec for an empty map</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::my-map</span> <span class="p">(</span><span class="nf">s/keys</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Empty map is valid, as expected</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::my-map</span> <span class="p">{})</span> <span class="c1">; => true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Extra keys are allowed as well</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::my-map</span> <span class="p">{</span><span class="ss">:example</span> <span class="s">"dog"</span><span class="p">})</span> <span class="c1">; => true</span>
</span></span></code></pre></div><p>If the extra keys have specs, they will be validated as well:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::my-map</span> <span class="p">(</span><span class="nf">s/keys</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::number</span> <span class="nv">int?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::my-map</span> <span class="p">{</span><span class="ss">::number</span> <span class="mi">2</span><span class="p">})</span> <span class="c1">; => true</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::my-map</span> <span class="p">{</span><span class="ss">::number</span> <span class="s">"two"</span><span class="p">})</span> <span class="c1">; => false</span>
</span></span></code></pre></div><p>This is great for many use cases, but it’s problematic for validating untrusted inputs.
There are two potential problem:</p>
<ol>
<li>An attacker may be able to set fields that they were not supposed to set.</li>
<li>An attacker may be able to make the validation very slow.</li>
</ol>
<p>The first problem is nothing new – I’ve seen it in hand-written validation code as well.
The second problem is clojure.spec-specific and I’m going to focus on it here</p>
<p>clojure.spec has support for structural regular expression specs with <code>s/cat</code>, <code>s/+</code> and others.
They’re usually used for writing specs for functions and implementing parsers in macros.
Unfortunately they also make spec vulnerable to <a href="https://en.wikipedia.org/wiki/ReDoS">regular expression denial of service</a> (ReDoS) attacks.</p>
<p>We can come up with a <a href="https://swtch.com/~rsc/regexp/regexp1.html">pathalogical regex</a>.
Here’s a Clojure vector equivalent of the regular expression <code>(0+)+1</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::slow</span> <span class="p">(</span><span class="nf">s/def</span> <span class="ss">::slow</span> <span class="p">(</span><span class="nf">s/cat</span> <span class="ss">:ones</span> <span class="p">(</span><span class="nf">s/+</span> <span class="p">(</span><span class="nf">s/+</span> <span class="o">#</span><span class="p">{</span><span class="mi">0</span><span class="p">}))</span> <span class="ss">:zero</span> <span class="o">#</span><span class="p">{</span><span class="mi">1</span><span class="p">})))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">time </span><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::slow</span> <span class="p">[</span><span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; "Elapsed time: 1932.420446 msecs"</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; true</span>
</span></span></code></pre></div><p>That’s slow, considering the input is a vector of 18 integers.
Worse, the asymptotic complexity of the algorithm seems to be <code>O(2^n)</code>.
If you add one more zero to the input, it takes twice as long to validate it.</p>
<p>Now, most likely you would not use regular expressions specs to validate untrusted input.
However, spec validates the extra map keys in the entity maps,
so if you have loaded a library that defines a slow spec,
an attacker may be able to craft an input that gets validated against it.</p>
<p>For example, <a href="https://github.com/gnl/ghostwheel">Ghostwheel</a> comes with some slow specs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; To load Ghostwheel: clj -Sdeps '{:deps {gnl/ghostwheel {:mvn/version "0.3.9"}}}'</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">ghostwheel.core</span> <span class="ss">:as</span> <span class="nv">g</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">time </span><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::g/some-unsafe-ops</span> <span class="p">(</span><span class="nb">repeat </span><span class="mi">35</span> <span class="ss">'let</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; "Elapsed time: 13896.968285 msecs"</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; true</span>
</span></span></code></pre></div><p><em>Note: I’m not here to pick on Ghostwheel.
Ghostwheel is using clojure.spec to parse macro input, which is an intended use case of clojure.spec.
It was the first example of pathologically slow specs in the wild that I could find, but
I’m sure there are more examples out there.</em></p>
<p>The input is a long list of <code>let</code> symbols.
If your untrusted input comes in as JSON, it won’t get converted into symbols.
However, many Clojure services use EDN or Transit, which support symbols.
The input is about 300 bytes of Transit.</p>
<p>Pulling a denial-of-service attack based on this requires specific circumstances:</p>
<ul>
<li>you process untrusted input encoded in EDN or in Transit, for example via a HTTP API,</li>
<li>you use entity maps (<code>s/keys</code>) to validate the input, and</li>
<li>you have loaded a library that defines slow specs.</li>
</ul>
<p>That does not describe every Clojure backend service I have ever seen,
but it’s not an unheard combination either.</p>
<p>As far as I can tell, Clojure itself does not come with pathologically slow specs.
Still, there are some slow-ish specs available in <a href="https://github.com/clojure/core.specs.alpha">core.specs.alpha</a>, which always gets loaded.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">time </span><span class="p">(</span><span class="nf">s/valid?</span> <span class="p">(</span><span class="nf">s/keys</span><span class="p">)</span> <span class="p">{</span><span class="ss">:clojure.core.specs.alpha/ns-clauses</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">repeat </span><span class="mi">100000</span> <span class="p">(</span><span class="nb">list </span><span class="ss">:gen-class</span><span class="p">))}))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; "Elapsed time: 4048.091583 msecs"</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; How big is the input? About 1.24 MiB</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">/ </span><span class="p">(</span><span class="nb">count </span><span class="p">(</span><span class="nb">pr-str </span><span class="p">(</span><span class="nb">repeat </span><span class="mi">100000</span> <span class="p">(</span><span class="nb">list </span><span class="ss">:gen-class</span><span class="p">))))</span> <span class="mf">1024.0</span> <span class="mf">1024.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => 1.2397775650024414</span>
</span></span></code></pre></div><h2 id="workaround">Workaround</h2>
<p>One of the new features in spec 2 is <a href="https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha#closed-spec-checking">the support for closed maps</a>.
That should improve the situation once it gets released.
Meanwhile, you can use <code>select-spec</code> from <a href="https://github.com/metosin/spec-tools/">spec-tools</a> to remove the extra keys:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; clj -Sdeps '{:deps {metosin/spec-tools {:mvn/version "0.10.5"}}}'</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.spec.alpha</span> <span class="ss">:as</span> <span class="nv">s</span><span class="p">]</span> <span class="o">'</span><span class="p">[</span><span class="nv">spec-tools.core</span> <span class="ss">:as</span> <span class="nv">st</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::good</span> <span class="nv">int?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::good-map</span> <span class="p">(</span><span class="nf">s/keys</span> <span class="ss">:opt-un</span> <span class="p">[</span><span class="ss">::good</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">st/select-spec</span> <span class="ss">::good-map</span> <span class="p">{</span><span class="ss">:good</span> <span class="mi">1</span>, <span class="ss">:bad</span> <span class="mi">2</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => {:good 1}</span>
</span></span></code></pre></div>Why take notes, anyway?
https://quanttype.net/p/smart-notes/
Sat, 27 Feb 2021 00:00:00 +0000https://quanttype.net/p/smart-notes/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sorsat4_hu_38f9cb19dd2b1a96.webp" type="image/webp" />
<img src="https://quanttype.net/images/sorsat4.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Recently I read Sönke Ahrens’s book <em>How to Take Smart Notes</em>. It is about
notetaking using the <a href="https://writingcooperative.com/zettelkasten-how-one-german-scholar-was-so-freakishly-productive-997e4e0ca125">Zettelkasten</a> method. The gist of the method is that you
note each idea on a separate card. Then you organize the cards into a hierarchy
and interlink them using a clever numbering system. This interlinking allows you
to generate new ideas. Nowadays you can use a computer program to do the same,
of course.</p>
<p>The book is thin on how the method actually works and focuses on <em>why</em> you
should use it. I’ve never been great at taking notes and the book gave me
insight into why is that: notetaking is part of a bigger process.</p>
<p>The purpose of Zettelkasten is not to remember what you have read. The purpose
is to be able to write. The book is geared towards students and researchers in
the academia and their main job is to write:</p>
<blockquote>
<p>Studying does not prepare students for independent research. It <strong>is</strong>
independent research. (p. 35)</p>
</blockquote>
<p>The book asserts that the writing process starts when you read something and
take notes about it. Zettelkasten is a way to convert what you’ve
learned from reading into writing of your own: first you make notes of
interesting ideas, then you develop those ideas by connecting them to your
existing ideas, and then you put that together that into a paper. You do all
this work using the interlinked note cards.</p>
<p>Now, I do not write papers, but I do have this blog, so Zettelkasten is relevant
for me. I’ve never thought about the ideas-to-blog pipeline in this way, but it
makes sense. However, if you do not read to write, or to speak, Zettelkasten
might not be the right method for you.</p>
<p>The book also asserts that <em>writing is thinking</em>. This rings true even if
writing is not the only way to think. To write about an idea, you have to
understand it. To write an argument, you have to confront the gaps in it.
If you want to think more or think better and you do not write much, you should
try writing more.</p>
The goal is to deliver working software
https://quanttype.net/p/goal-is-to-deliver-working-software/
Sat, 20 Feb 2021 00:00:00 +0000https://quanttype.net/p/goal-is-to-deliver-working-software/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaalla_hu_accb20b552f7db45.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaalla.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>As software engineers, how do we evaluate new technologies, programming
languages, and practices such as <a href="https://quanttype.net/tags/code-review.html">code review</a>? We must keep our goal in mind.</p>
<p>Our goal is to <em>deliver working software</em>. We need to achieve this goal with
limited resources: we have only so much time, manpower, and computing capacity
available.</p>
<p>The goal is not to perfectly follow a proceduce described by book.
The goal is not to craft the perfect masterpiece of code.
The goal is not to make you feel smart, either.</p>
<p>Forgetting this leads to arguments that look at the technolgies and the
practices in isolation but forget about their context.</p>
<hr>
<p>For example, at work I’ve thought about splitting up a Scala web service and
re-implementing a part of it in Rust. The resulting microservice would be
simple, but it would be under heavy load. It is one of the hottest nodes in our
graph of services, so great performance is important.</p>
<p>Just looking at the technology, this seems like a no-brainer: surely Rust would
offer us greater and more predictable performance than Scala on JVM. The size
of the service would be so small that there would be minimal risk of screwing it
up.</p>
<p>On the other hand, our team has zero Rust experience right now. Our JVM
expertise does not transfer to operating Rust services. We would need to learn
how to do it and keep the team staffed with people who are able to and want to
work with Rust for years to come.</p>
<p>Is it worth it? It could be, it could be not.</p>
<hr>
<p>On the other hand, I’m not here to argue that the ends justify any means. The
goal may not be to make you feel smart, but software engineering is not supposed
to hurt, either.</p>
<p>Consider crunching, the practice of working overtime to meet a deadline. It is
common in the games industry where the games have big bang launch dates. Twitter
is full of stories about how miserable this has been for everyone involved. They
were trying deliver working, delightful software, but was the price acceptable?</p>
Winter-posting
https://quanttype.net/p/winter/
Sat, 13 Feb 2021 00:00:00 +0000https://quanttype.net/p/winter/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sukset_hu_c4b67b394fa1d1b6.webp" type="image/webp" />
<img src="https://quanttype.net/images/sukset.jpg" width="600" height="450" alt="The tips of cross-country skis in the snow." loading="lazy" />
</picture>
</figure>
<p>Mustikkamaa is one my favorite spots in Helsinki. It’s a small island that is
connected to Helsinki by Isoisänsilta, the white bridge that is in many of my
blog pictures. There’s a nice walking path along the shore that I like to
follow.</p>
<p>This winter has presented us with a rare treat: the sea has frozen.</p>
<p>The ice is covered by snow, with a couple of buoys and sea marks sticking up here and
there. Only at the shoreline and under the bridges there are a few brown spots
of ice. The snow is marked by footprints and ski tracks – people have
seized to opportunity to walk on the ice. I, too, did so just today.</p>
<p>Near the bridge to Korkeasaari Zoo, there was a family with fishing. They had
drilled two holes, with two of the kids sitting still around one of the holes.
“Something is pulling”, they would shout. “It is pulling <em>right now</em>!” The
other two kids were swinging their rods wildly over the other hole. I think
they were more likely to find success as dancers than as fishers.</p>
<p>Every few minutes, there was a sound of swooshing. The cross-country skiers
looking for exercise were passing by on the track ashore. On the walking path,
there were runners and couples pushing prams. Everybody else – the flâneurs –
were on the ice, either walking or skiing.
They were taking photos of themselves and the ice.</p>
<p>Between Mustikkamaa and Korkeasaari, there are two big rings made from snow.
Somebody has ploughed the snow inside the rings. Why? I don’t know. They’re a
bit small for skating rinks. Maybe they look like something from the air. Maybe
they are art? At least everybody passing by stopped and took a look at them.</p>
<p>The ice won’t be here too long. Better enjoy it while it lasts.</p>
When to not use code review
https://quanttype.net/p/when-to-not-use-code-review/
Sat, 06 Feb 2021 00:00:00 +0000https://quanttype.net/p/when-to-not-use-code-review/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lumipuut_hu_56f14d384b93ede.webp" type="image/webp" />
<img src="https://quanttype.net/images/lumipuut.jpg" width="600" height="400" loading="lazy" />
<figcaption>Code review is not a panacea, unfortunately.</figcaption>
</picture>
</figure>
<p>I talk a lot about <a href="https://quanttype.net/tags/code-review.html">code review</a> and think that it’s a great tool, but I want to
re-iterate a point I made previously: <a href="https://quanttype.net/posts/2020-12-13-code-review-in-context.html">code review is not the right tool in
every situation</a>.</p>
<p>What I mean by code review is the widely-used, pull request style workflow.</p>
<p>When you want to integrate your code changes to the main branch, you post your
changes to a code review tool, for example by creating a pull request in GitHub.
Your teammates then review your change and either approve it or ask you
for some improvements. Once the change has been approved, it is merged to the
main branch.</p>
<p>Furthermore, I’m talking about professional software development teams shipping software-based
products or services. Thus, my advice won’t directly apply to e.g. open-source
development. The same workflow is popular in the open-source world, but the
relationship between open-source collaborators is different from the relationship
between the members of a professional team.</p>
<p>In this context, what are some situation where code review is not beneficial?</p>
<p><strong>High-velocity collaboration over a small code base.</strong> For example, when you’re
starting a new project, there’s a lot of scaffolding to set up and many new
things to build. Typically the details do not yet matter much and changing
things is easy. Code review is too slow and too detail-oriented - you’re better
off with talking to each other and reading each others code after it has been
merged to the main branch.</p>
<p><strong>When code review does not do anything.</strong> If getting your changes reviewed takes days
and the result is just “LGTM” (looks good to me), the review process is not
bringing you any value. Slow reviews are demoralizing, as finishing any task
takes seemingly forever, and they hurt the team’s ability to ship.</p>
<p>You could try fixing the broken process, but the simplest way to
improve the situation is to stop reviewing altogether. There’s an emotional
barrier to actually stopping, but if the reviews are not providing any value, it
is a safe step to take.</p>
Writing is a core skill for developers
https://quanttype.net/p/writing-core-skill/
Sat, 30 Jan 2021 00:00:00 +0000https://quanttype.net/p/writing-core-skill/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kivi2_hu_fbbaa8b8db1161dc.webp" type="image/webp" />
<img src="https://quanttype.net/images/kivi2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>We software developers write a lot of code, but we write text, too. We write
issues, commit messages, specifications, references, docs, and architectural
decision records. We write chat messages and e-mails. Some of us even blog!</p>
<p>That’s a lot of writing and it would be great if the writing was good. When
working remotely, it matters even more, since you can’t solve everything by
talking. This is a bit of a blind spot for some: a person can be a great
communicator in person, yet they may not be comfortable with text.</p>
<p>It looks like the pandemic and remote work are going to be with us for a while,
so it’s worth it to look at how your everyday writing is doing. It does not have
to be eloquent, but it has to be effective. Does it get your point across
quickly and clearly?</p>
Clojure project automation tool of my dreams
https://quanttype.net/p/clojure-project-management-tool-of-my-dreams/
Thu, 14 Jan 2021 00:00:00 +0000https://quanttype.net/p/clojure-project-management-tool-of-my-dreams/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kivikuva_hu_deaa69b82075e16.webp" type="image/webp" />
<img src="https://quanttype.net/images/kivikuva.jpg" width="600" height="400" alt="Large snow-covered rock sitting in the frozen sea near shore." loading="lazy" />
</picture>
</figure>
<p>For a long time, <a href="https://leiningen.org/">Leiningen</a> was the Clojure project automation tool almost
everyone used. Clojure itself did not come with a tool for
managing dependencies – you had to use Leiningen, Maven, or Boot, or download
the dependencies yourself and construct the classpath by hand.</p>
<p>This changed when Clojure 1.9 was released in 2017. It included the new
<code>clojure</code> command-line tool that supported the <code>deps.edn</code> file for declaring
dependencies. Many people started to use the new tool instead of
Leiningen.</p>
<p>Leiningen is straightforward to use and it has a good set of features and a nice
plugin ecosystem. However, it is a heavy tool if all you want to do is to run
some Clojure code. <code>deps.edn</code> was created to solve this problem: it offers a
lightweight way to run Clojure code that uses external libraries.</p>
<p>I think it’s great: in addition to being nimbler than Leiningen, the dependency
information is now data and it supports git dependencies.</p>
<p>It is not a replacement for Leiningen, however. It is not, and it was never
meant to be, a full-fledged project automation tool. I’ve used it quite a bit
and I find myself missing many of nice features of Leiningen. In theory you
could use Leiningen and <code>deps.edn</code> together with <a href="https://github.com/RickMoynihan/lein-tools-deps">lein-tools-deps</a> but in
practice it has turned out to be awkward.</p>
<p>Here are a few things that I’m missing from Leiningen:</p>
<ul>
<li><code>lein new</code> for setting up a new project</li>
<li><code>lein repl</code> for starting a featureful, nREPL-enabled REPL</li>
<li><code>lein ubejar</code> for building jars with dependencies included - essential part of many deployment workflows</li>
<li><code>lein install</code>, <code>lein deploy</code>, and <code>lein release</code> for publishing the project to a Maven repo such as Clojars</li>
</ul>
<p>The community has built <a href="https://github.com/clojure/tools.deps.alpha/wiki/Tools">versions of all of these</a> for <code>deps.edn</code>.
However, you have to set each of them up separately and they do not form a coherent whole.</p>
<p>What I would love to see is a new tool – Leiningen 3.0, if you will – that would
resolve this tension. There must be a way to build full-featured, user-friendly
project automation tool on top of <code>deps.edn</code>.</p>
<p>I’d like to see is something with simple setup, with convention over configuration.
Clojure is all about configuration and making things your own, but Leiningen’s strength comes from its good defaults.
It is a very configurable tool but a lot of people have been happy with the default settings.
That makes it easy to jump between projects, too.</p>
Yearnote 2020
https://quanttype.net/p/yearnote-2020/
Wed, 06 Jan 2021 00:00:00 +0000https://quanttype.net/p/yearnote-2020/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jatkankynttila_hu_938fbf4d9ee5b41f.webp" type="image/webp" />
<img src="https://quanttype.net/images/jatkankynttila.jpg" width="600" height="400" loading="lazy" />
<figcaption>We didn’t have fireworks, but at least we had a <em>jätkänkynttilä</em> for New Year’s Eve.</figcaption>
</picture>
</figure>
<p>2020 was definitely a year. <a href="https://quanttype.net/posts/2020-01-05-yearnote-2019.html">As is my habit</a>, I want to reflect a
bit on how it was.</p>
<p>I was lucky and privileged in that my year wasn’t that bad. It was easy for me
to start working remotely, the business was good, and me, my close friends and
my family did not have big health problems, due to COVID-19 or otherwise. Many
people had a way worse year than I did.</p>
<p>Still, it would have been nice to meet people in person. It would have been nice
to <em>do things</em>.</p>
<hr>
<h2 id="working-life">Working life</h2>
<p>I worked with two big Finnish companies. I switched from one to the other during
the lockdown in April. It’s a bit odd that I’ve only met the people I work with
once – we had a lunch together in September – but we’ve got the work <em>working</em>
nicely.</p>
<p>It was great to get to finally try out remote working for real. It suits me
well: I enjoy the lack of commute and the peace of my home compared to open-plan
offices. Remote work forces you to communicate more explicitly and to document
things better, but that’s just a plus in my book. I just wish we had more space
at home. I would love to have a study.</p>
<p>Will I return to the office in 2021? I don’t know. There’s so much uncertainty
about the vaccination schedule that it’s hard to say when it would be possible.
It could make sense to take steps to make remote-by-default a permanent
arrangement.</p>
<h2 id="open-source">Open source</h2>
<p>I published <a href="https://sr.ht/~miikka/clj-branca/">clj-branca</a> and <a href="https://sr.ht/~miikka/clj-base62/">clj-base62</a>, but apart from that, I didn’t do
much. I wasn’t really in the mood for working on open source in my free time,
and at Metosin, there were more pressing needs than open source.</p>
<p>At Metosin, we released <a href="https://github.com/metosin/malli">Malli</a> (my contributions were minor). It was great to
see such positive reception from the Clojure community!</p>
<h2 id="free-time">Free time</h2>
<p>I had a lot of time for hobbies this year. I read <a href="https://quanttype.net/posts/2020-08-02-a-couple-of-books.html">more books</a> than ever. I wrote more blog posts than ever, many of
them technical. Here are a couple of my favorite ones about Clojure:</p>
<ul>
<li><a href="https://quanttype.net/posts/2020-05-10-essential-features.html">Essential features of data specification libraries</a></li>
<li><a href="https://quanttype.net/posts/2020-07-26-signing-jars-is-worthless.html">Signing .jars is worthless</a></li>
<li><a href="https://quanttype.net/posts/2020-09-20-local-memoized-recursive-functions.html">Local memoized recursive functions</a></li>
</ul>
<p>I wrote more <a href="https://twitter.com/arcatan/status/1335257969278193668">Christmas cards</a> than ever. I started to write <a href="https://quanttype.net/posts/2020-11-29-morning-pages.html">morning pages</a>,
too. That has turned out to be a benifical practice. I had this feeling of “I
wish I would think more” and the morning pages were exactly the structure that I
needed.</p>
<p>In the summer, I <a href="https://quanttype.net/posts/2020-07-19-on-paddling.html">kayaked</a> more than ever. It became a <a href="https://www.youtube.com/watch?v=NVGuFdX5guE">theme</a> for the summer:
when I didn’t know what to do, I went to kayak. I cooked more than ever. Thanks to the transition to remote work, I started to
eat <a href="https://twitter.com/arcatan/status/1295799910370086914">lunch</a> at home. It also meant that I have washed more dishes than ever.</p>
<p>I exercised a fair bit, although probably not more than ever, and even bought a kettlebell.</p>
<p>Regretfully, I did not hike more than ever. I had hoped to hike the northmost
section of the <a href="https://en.wikipedia.org/wiki/Kungsleden">Kungsleden</a> trail but had to cancel it when travel to Sweden was
restricted. I did hike the Kaakkurinkierros loop around <a href="https://www.nationalparks.fi/repovesinp">Repovesi National
Park</a>, though. We were there on some of the hottest days of the summer
and it was great. The numerous small lakes offer a lot of opportunities for
swimming.</p>
<p>In 2021, I hope to hike more then ever. I’ll go to Kungsleden if that
is possible but if not, Finland has plenty of interesting trails.</p>
<h2 id="whats-next">What’s next?</h2>
<p>2020 ended with expectant mood. Who knows what will happen in 2021? I feel
hopeful that we can make it a better year than the previous one.</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p>I’m not surprised that Sanna Marin’s cabinet has held together. I think they’ve
done pretty good job with the pandemic. Were there even any other political
themes this year?</p>
NixOS impressions
https://quanttype.net/p/nixos-impressions/
Sun, 20 Dec 2020 00:00:00 +0000https://quanttype.net/p/nixos-impressions/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/silta2_hu_fa4378da11120380.webp" type="image/webp" />
<img src="https://quanttype.net/images/silta2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>In February, I got a new laptop for home use and installed NixOS on it. This was
the first time I have ever used NixOS. In this post, I’ll share some thoughts
about how it has worked for me.</p>
<p>The laptop is a second-hand ThinkPad X250. Installing NixOS was pretty
straightforward and I haven’t had any trouble the hardware compatibility.</p>
<p><strong>Good.</strong> Configuring your system with a centralized configuration file is a
pleasure. It offers a unified way to configure everything and a short config
goes a long way. If you like Infrastructure as Code, you will like this.
You can see my <a href="https://github.com/miikka/dotfiles/blob/master/configuration.nix">configuration.nix</a> here.</p>
<p>Another great feature is that you can roll upgrades and configuration changes
back. Just today I upgraded my packages (<code>nixos-rebuild switch --upgrade</code>),
rebooted the laptop, and the system didn’t come up anymore. It got stuck
waiting for udev, whatever that means. I rebooted the system again, selected the
previous generation from the boot menu, and the system started again. I will
have to face the broken udev eventually – hopefully a later upgrade will fix it
– but at least the system works for now.</p>
<p><strong>Bad.</strong> Let’s face it: Nix and NixOS are doing their own thing. Your skills
with other Linux distros won’t directly transfer. You will be tempted to say
“eh, I’ll just edit this file and run this command” and you will be frustrated
when you can’t find those files and those commands do not work.
Learning to operate the system and make your own packages takes a while.</p>
<p>Since I’m mostly using this laptop for writing and surfing the web, I have not
bothered to learn much. I did manage to package and install <a href="https://github.com/k4rthik/git-cal">git-cal</a> with Nix,
but have not looked into how to make that package available for others. Should I
contribute it to the main nixpkgs repo? I don’t know.</p>
<p><strong>Ugly.</strong> The tooling does not put much emphasis on the versions of packages.
You’ll get a package, sure, but whatever is the version is secondary. If I was
using NixOS for development, I’d like to pin the versions of the tools I’m using
like I pin the versions of the libraries. Apparently the upcoming <a href="https://nixos.wiki/wiki/Flakes">Nix Flakes</a>
feature offers a decent solution, but I was suprised that this wasn’t a
solved problem.</p>
<p>Overall, Nix and NixOS remind me of Clojure: they’re powerful, they do things
differently from what you’re used to, and learning them is… complicated. I’ll
leave it for another time to debate whether it is worth it.</p>
Code review in context
https://quanttype.net/p/code-review-in-context/
Sun, 13 Dec 2020 00:00:00 +0000https://quanttype.net/p/code-review-in-context/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mustikkamaa_hu_7263c9a20a43ef9b.webp" type="image/webp" />
<img src="https://quanttype.net/images/mustikkamaa.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I’ve posted about <a href="https://quanttype.net/tags/code-review.html">code review</a> a number of times, about how it <a href="https://quanttype.net/posts/2015-12-16-code-review-requires-trust.html">requires trust</a>
and <a href="https://quanttype.net/posts/2019-12-05-keeping-code-review-fast.html">needs to be fast</a>. But, in the end of the day, code review is just <a href="https://quanttype.net/posts/2020-11-22.html">one
tool in the toolbox of collaborative software development</a>. It should
be evaluated in the context in which it is used.</p>
<p>For example, when you’re starting a new project from scratch, usually there’s a lot
scaffolding to set up. This is an important time for collaboration as you’re
laying the foundation for building new things. However, most of the code
written at this stage will be boilerplate. Detail-focused PR-based code review
will be a hinderance: there’s no point in line-by-line critique of boilerpalte
and it’s too slow. You’ll be better off pair coding and iterating quickly. Maybe
simply regularly talking to each other is all you need!</p>
<p>Whether code review is the right tool for you and how it should be done depends
on the team, the goals, the environment, and the stage of the project. This is
why I believe that <em>the team should choose its own tools</em> and continuously
evaluate them. Regular retrospectives are a great way to make this happen.</p>
<hr>
<p>I’ve realized that I give a lot of advice that assumes a skilled team in a
psychologically safe environment. It works to an extent for less skilled teams as
long as the environment is safe and they want to learn.</p>
<p>But what if the environment is not safe? I’m pretty sure you’ll have to start by
fostering safety, but I don’t know how that is done.</p>
New shell prompt with Starship
https://quanttype.net/p/new-shell-prompt/
Sun, 06 Dec 2020 00:00:00 +0000https://quanttype.net/p/new-shell-prompt/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/branch_hu_37882b04afedf1ad.webp" type="image/webp" />
<img src="https://quanttype.net/images/branch.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I’ve used zsh for at least 15 years. Occasionally I’ve toyed with the idea of
switching to some of the newer shells such as <a href="https://fishshell.com/">fish</a> (“Finally, a command line
shell for the 90s”) or <a href="https://www.nushell.sh/">nushell</a>. I’m stuck in my ways, though, so despite all
the cool features and ease of use promised by the other shells, I’ve kept using
zsh.</p>
<p>I decided to at least freshen up my shell prompt. In 2012, I added git branch
information to my prompt using zsh’s <a href="https://github.com/miikka/dotfiles/blob/b4fc83b76bc3e8d27f9f46ff4ef8b31e4411e7d3/core/.zshrc.prompt#L1-L8">vcs_info</a>. However, I never got around to
configuring nice prompts for different git states like rebase.</p>
<p>It was time to fix this. Instead of diving into vcs_info, I decided to use
<a href="https://starship.rs/">Starship</a>, a cross-shell tool that produces shell prompts for you.</p>
<p>Its default configuration is spammy – for some reason, it insists on displaying
the version of the programming language implementation you’re using when you’re
in a project directory. For example, if you’re in a directory with PHP files, it
shows the version of PHP you have installed. This is information that I almost
never need.</p>
<p>However, Starship is easy to customize and you can disable all this stuff. My
<a href="https://github.com/miikka/dotfiles/blob/master/core/.config/starship.toml">configuration</a> has now a list of blocks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">php</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">disabled</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>What is nice is that Starship’s git support is good and shows useful information
like whether you’re ahead or behind of the upstream branch. I constantly use
<code>git status</code> (well, my custom alias <a href="https://github.com/miikka/dotfiles/blob/b4fc83b76bc3e8d27f9f46ff4ef8b31e4411e7d3/core/.gitconfig#L5"><code>git st</code></a>) to check this, so it
makes sense to put it in the prompt. The downside is that this makes your prompt
slow when entering big repositories.</p>
<p>Another nice touch is that it only shows your hostname when you connect over
SSH. This is something I had been meaning to make my prompt do for ages but
never got around to it.</p>
<p>My prompt used to look like this:</p>
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/shell-old.png" width="600" height="419.6480938416422" loading="lazy" />
</picture>
</figure>
<p>Now it looks like this:</p>
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/shell-new.png" width="600" height="407.2100313479624" loading="lazy" />
</picture>
</figure>
<p>Yes, I did configure it to be exactly the same. If I want to add some new stuff,
now it actually feels doable. And as a pleasant surprise, the new prompt feels
as fast as the old one (outside of big git repos)!</p>
<p>If your prompt needs tweaking, I recommend taking a look at Starship – just
take your time with the configuration.</p>
Early impressions on morning pages
https://quanttype.net/p/morning-pages/
Sun, 29 Nov 2020 00:00:00 +0000https://quanttype.net/p/morning-pages/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/vihko_hu_7cb039fc9f4183d4.webp" type="image/webp" />
<img src="https://quanttype.net/images/vihko.jpg" width="600" height="400" alt="A yellow notebook laying on a table with a black ballpoint pen on top of it." loading="lazy" />
</picture>
</figure>
<p>A couple of months ago, I was feeling that I should <em>think more</em> about things.
It was not that I was too busy to think – the problem was that my thinking was
scatter-brained.</p>
<p>Since then, I’ve discovered a practice that helps. It’s called <strong>morning pages</strong>
and the idea is this: every morning, you sit down, grab a pen, and write three
pages. Just write about whatever pops in your mind.</p>
<p>Basically it’s a journaling pratice. The idea comes from the book <em>The Artist’s
Way</em> by Julia Cameron. I have not read the book, but, as far as I can tell, you
do not need to read the book to use the method.</p>
<p>Writing morning pages does two things for me:</p>
<ul>
<li>It offers me a moment every day to sit down and be in touch with the stuff
that is on my mind.</li>
<li>Writing it down helps me to focus and actually pursue lines of thought instead
of just jumping around.</li>
</ul>
<p>Topics I’ve written about range from the taste of my morning coffee to my
thoughts on company strategy. There’s no wrong topic to write about, it’s just
what happens to be on my mind on any given morning.</p>
<p>I’ve tried it a few times before, but this time I’ve stuck to it. Since I
started in early November, I’ve written 16 times – not every day, but almost.
It’s working so well that my intention is to continue until I’ve done it <a href="https://twitter.com/visakanv/status/1199206638458036224">100
times</a>.</p>
<p>In practice, I write three pages with a pen in an A5 size notebook. I write in
my native Finnish. It takes me about half an hour to write the 350 or so words.
When I wake up, first I have breakfast and then I write; but if I can’t write in
the morning, I try to do it later in the day.</p>
<p>It’s worth it to experiment a bit with the specifics. For example, I tried to
write on the computer, but I found that I prefer the limit of three physical
pages to a word limit on the screen. There’s something satistfying about filling
a notebook, too.</p>
<p>It’s better to have a length limit instead of a time limit to ensure that you
actually write something. If nothing else pops in your mind, you can write about
how nothing else pops in your mind.</p>
<p>You don’t need a fancy pen or a fancy notebook to write your morning pages, but
if you are into such things, morning pages are a great opportunity to use them.
If you don’t want to keep a written record of your thoughts around, you can use
loose sheets and put them into a shredder once you’re done. That’s okay, too.</p>
<p>I’ll report back in March when I’ve completed the 100-morning project.
Meanwhile, you should give it a go.</p>
Code review is for collaboration
https://quanttype.net/p/code-review-is-for-collaboration/
Sun, 22 Nov 2020 00:00:00 +0000https://quanttype.net/p/code-review-is-for-collaboration/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sign_hu_c1999f3b75be569.webp" type="image/webp" />
<img src="https://quanttype.net/images/sign.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>A lot of people see code review as a gatekeeping step in the software
development process. They think that the reviewer is there to prevent bugs from
getting into production. This is not a good way to think about it.</p>
<p>Code review is an opportunity for collaboration. The task of the reviewer is to
<em>work together with the author</em> to produce great software. Identifying flaws is
a part of that, as is finding ways to address them.</p>
<p>If the goal was to prevent shipping bugs, you could just block every change. It
guarantees that no bugs are shipped – but no bugfixes or features are shipped,
either.</p>
Why bother with Integrant?
https://quanttype.net/p/why-use-integrant/
Sun, 15 Nov 2020 00:00:00 +0000https://quanttype.net/p/why-use-integrant/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/havut_hu_9565e2f0a3e735ed.webp" type="image/webp" />
<img src="https://quanttype.net/images/havut.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>The Clojure backends that I’ve worked on have often had a number of
subcomponents: a HTTP server such as Jetty, a database connection pool, a
scheduler, a message queue processor and so on. When you start the backend, you
need to start all those components. You may have seen or written code like this
to do so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">start</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">connection-pool</span> <span class="p">(</span><span class="nf">connection-pool/create</span> <span class="nv">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">stop-scheduler</span> <span class="p">(</span><span class="nf">scheduler/create</span> <span class="nv">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">ring-handler</span> <span class="p">(</span><span class="nf">create-handler</span> <span class="nv">connection-pool</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">stop-server</span> <span class="p">(</span><span class="nf">jetty/start</span> <span class="nv">ring-handler</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">stop-server</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">stop-scheduler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.stop</span> <span class="nv">connection-pool</span><span class="p">))))</span>
</span></span></code></pre></div><p>This function starts a bunch of service components and returns a function that
stops those components when called. As you probably know, the same problem can
be solved with <a href="https://github.com/weavejester/integrant">Integrant</a>. This works, though, so why bother?</p>
<p><strong>Correct start-up and shut-down order</strong>. In the example, to create the Ring
handler, you need to first create a connection pool and then pass it to the
handler. They should be closed in the opposite order. Integrant keeps track of
this for you and automatically starts and stops everything in the correct order.</p>
<p><strong>Starting a partial system</strong>. In the example above, if you only want to start
the HTTP server but not the scheduler - maybe for development or tests - you
need to create another function that takes care of that. In Integrant, all you
need to do is to call <code>init</code> with a sequence of keywords specifying the
components that you want to start.</p>
<p><strong>Good for REPL-driven and test-driven workflows.</strong> If you run a REPL and your
tests, or if you run your tests in parallel, in the same JVM, you’ll probably
want to start separate instances of your service for each of those. On the other
hand, for REPL-driven development, you’ll probably want to have a single global
instance so that it is easy to poke. You can set it up by hand… or you can use
Integrant and <a href="https://github.com/weavejester/integrant-repl">integrant-repl</a>.</p>
<p>You do not <em>need</em> to use Integrant (or its alternatives like <a href="https://github.com/tolitius/mount">mount</a>). It’s easy
enough to write something passable by hand. But using it will cut down boilerplate,
enable nice workflows, and probably squash some subtle bugs as well.</p>
Two albums of sad music
https://quanttype.net/p/two-albums/
Sun, 08 Nov 2020 00:00:00 +0000https://quanttype.net/p/two-albums/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lehdet3_hu_9a65cc587db3700a.webp" type="image/webp" />
<img src="https://quanttype.net/images/lehdet3.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Looking for something to listen? Here are two great, slightly sad albums.</p>
<p>This year has not been like the other years. Maybe it’s the gravity of COVID-19,
but I’ve found myself listening to sorrowful music than before. That’s the
mood I associate with these two albums, one of them by a duo of folk musicians and the
other by a jazz pianist.</p>
<p><strong>Mielo</strong> by <em>Maria Kalaniemi</em> and <em>Eero Grundström</em> is full of yearning to the
wilderness. Kalaniemi plays an accordion and Grundström plays a harmonium and a
modular synthesizer. The album’s weird energy comes from the combination of the
traditional instruments and the beat of the synthesizer.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><em>Listen on:</em> <a href="https://open.spotify.com/album/466MM3ePZNaC671M4a8HQm?si=KEWsbwACSEyd_wFb4JdrhA">Spotify</a>.</p>
<p><strong>My Finnish Calendar</strong> by <em>Iiro Rantala</em> consists of 12 tracks, one for each
month. January starts with melancholy but the year has many moods and already
February is full of joy and energy. My favorite track, September, lies somewhere
in between. Rantala combines piano with some light percussion. If you’re
familiar with his music, the album definitely sounds like him although it’s
easier-going than some of his work.</p>
<p><em>Listen on:</em> <a href="https://iirorantala.bandcamp.com/album/my-finnish-calendar">Bandcamp</a>
or <a href="https://open.spotify.com/album/1rInq2apaCIU3Rr47GXBeT">Spotify</a></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This is not the only time Eero Grundström has been experimenting with
folk and synthesizers. For a very different take, check out <a href="http://www.suistamonsahko.fi/">Suistamon
sähkö</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
What are DIDs?
https://quanttype.net/p/what-are-dids/
Sun, 01 Nov 2020 00:00:00 +0000https://quanttype.net/p/what-are-dids/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lehti3_hu_77d489c4a16bab1f.webp" type="image/webp" />
<img src="https://quanttype.net/images/lehti3.jpg" width="600" height="400" alt="Yellow maple leaves lying on the ground" loading="lazy" />
</picture>
</figure>
<p>Have you heard of <a href="https://www.w3.org/TR/did-core/">Decentralized Identifiers</a> (DIDs)? They’re a
work-in-progress W3C recommendation that I’ve seen pop up in a couple places -
mostly recently in the just-updated <a href="https://www.thoughtworks.com/radar/techniques/decentralized-identity">Thoughtworks Technology Radar</a>.
Since I’ve been interested in technologies related to identity and access
management, I thought I should take a look.</p>
<h2 id="why-do-you-need-identifiers">Why do you need identifiers?</h2>
<p>When you want to refer to a subject, for example a person, in a data processing
system, you need an identifier for them. If you’re operating in a single system,
you can get away with a <em><a href="https://en.wikipedia.org/wiki/Surrogate_key">surrogate key</a></em>: an arbitrary identifier assigned by
the system. For example, in a typical web application each user gets assigned an
user ID. Usually it’s either randomly generated or based on an incrementing
counter.</p>
<p>If you’re need to exchange data between multiple systems, the systems
need to be able to refer to the same subject with the same identifier. One
solution is to have a central authority that issues identifiers. For books, this
could be ISBN. For humans, it can mean using national identification number such
as <em><a href="https://en.wikipedia.org/wiki/National_identification_number#Finland">henkilötunnus</a></em> in Finland<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> or using their account elsewhere as the
identifier via services like Facebook Login.</p>
<p>What if you do not want to be tied to a central authority, or no suitable
authority exists? Self-sovereign or decentralized identifiers could be the
solution. The idea is to create unique identifiers such that the controller of
the identifier can cryptographically prove that they control it. DID is an
attempt to create a standard framework for such identifiers.</p>
<h2 id="what-are-dids-like">What are DIDs like?</h2>
<p><em>Note: I’m basing this post on the version “W3C Working Draft 27 October
2020” of the DID specification.</em></p>
<p>DID identifiers (“DIDs”) are URIs and they look like this:</p>
<pre tabindex="0"><code>did:example:123456789abcdefghi
</code></pre><p>There are three parts separated by colons. The first part, <code>did</code>, identifies
the URI scheme. The second part, <code>example</code>, identifies the DID method, and the
last part, <code>123456789abcdefghi</code>, is the method-specific identifier.</p>
<p>Each DID is associated with metadata (“DID document”) and the DID method tells
you how to find (“resolve”) that metadata. The specification itself does not
specify any methods but there already are plenty of them in the <a href="https://www.w3.org/TR/did-spec-registries/#did-methods">DID Method
registry</a>. Most of them seem to be blockchain-based, but there’s e.g.
the <a href="https://docs.github-did.com/did-method-spec/">github-did</a> that looks up a specially-named file in a specially-named repo
for the given user. My GitHub account is <a href="http://github.com/miikka"><code>miikka</code></a>,
so I could control the DID <code>did:github:miikka</code> if I created a suitable file.</p>
<p>The specification defines the data model for a DID document and includes three
serialization formats (“representations”): JSON, JSON-LD, and CBOR.</p>
<p>You can, of course, define your own representations if you want. If the Clojure
community would find use for DIDs, I imagine somebody would quickly define an
EDN represetation.</p>
<p>Here’s a JSON-LD example of DID document for <code>did:example:123456789abcdefghi</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"@context"</span><span class="p">:</span> <span class="s2">"https://www.w3.org/ns/did/v1"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"id"</span><span class="p">:</span> <span class="s2">"did:example:123456789abcdefghi"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"authentication"</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"id"</span><span class="p">:</span> <span class="s2">"did:example:123456789abcdefghi#keys-1"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"type"</span><span class="p">:</span> <span class="s2">"Ed25519VerificationKey2018"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"controller"</span><span class="p">:</span> <span class="s2">"did:example:123456789abcdefghi"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"publicKeyBase58"</span><span class="p">:</span> <span class="s2">"H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Under <code>authentication</code>, there’s a Ed25519 public key that can be used to verify
that somebody is acting on behalf of the subject of this DID. Again, there are
<a href="https://www.w3.org/TR/did-spec-registries/#verification-method-types">plenty of verification methods</a> if Ed25519 does not float your boat.</p>
<h2 id="whats-the-point">What’s the point?</h2>
<p>There’s a related standard called <a href="https://www.w3.org/TR/vc-data-model/">Verifiable Credentials</a> (VCs). They’re a
way to issue cryptographically verifiable claims about a subject and, well, you
need to able to identify the subject. I believe this is the origin of DIDs.</p>
<p>There’s a list of <a href="https://www.w3.org/TR/vc-use-cases/">use cases for VCs</a>. They include stuff like
universities issueing VCs to certify that a person has completed a degree.</p>
<p>At this point, I’m not sure what to think. Does this solve a real problem in
such a way that people are willing to use it?</p>
<p>In any case, this is a framework with so many options that if you intend to
build something actually interoperable on it, you should start by defining a
profile of what representations, verification methods, resolution methods etc.
have to be implemented and should be used for your use case.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Are you a citizen of Finland? Consider signing this <a href="https://www.kansalaisaloite.fi/fi/aloite/7548">initiative to outlaw
using HETU to authenticate people</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Caching HTTP requests in Clojure
https://quanttype.net/p/caching-http-requests/
Sun, 25 Oct 2020 00:00:00 +0000https://quanttype.net/p/caching-http-requests/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lehti1_hu_85eb24c59e912dac.webp" type="image/webp" />
<img src="https://quanttype.net/images/lehti1.jpg" width="600" height="400" alt="Yellow leaves on moss" loading="lazy" />
</picture>
</figure>
<p>Sometimes you want to cache the results of a function with side-effects. For
example, you might cache the results of HTTP requests or database queries.</p>
<p>If you’re using Clojure, you might reach for <a href="https://github.com/clojure/core.cache">core.cache</a>. The caches created by
core.cache are supposed to be wrapped in an atom, so you would write something
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.core.cache</span> <span class="ss">:as</span> <span class="nv">cache</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">http-get</span> <span class="p">[</span><span class="nv">url</span><span class="p">]</span> <span class="nv">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; cache the results for a minute</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">my-cache</span> <span class="p">(</span><span class="nf">atom</span> <span class="p">(</span><span class="nf">cache/ttl-cache-factory</span> <span class="p">{}</span> <span class="ss">:ttl</span> <span class="mi">60000</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">fetch-data</span> <span class="p">[</span><span class="nv">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">-> </span><span class="p">(</span><span class="nf">swap!</span> <span class="nv">my-cache</span> <span class="nv">cache/through-cache</span> <span class="nv">url</span> <span class="nv">http-get</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">cache/lookup</span> <span class="nv">url</span><span class="p">))</span>
</span></span></code></pre></div><p>If you try it, it seems to work. There’s a bug, though, but you will likely
notice it only under high load. Can you spot it?</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lehti2_hu_f0e648330110d915.webp" type="image/webp" />
<img src="https://quanttype.net/images/lehti2.jpg" width="600" height="400" alt="A close-up of a yellow leave with droplets on it" loading="lazy" />
</picture>
</figure>
<p>The problem is that <code>swap!</code> may re-run the function that is passed to it. This
may cause a variaton of <a href="https://en.wikipedia.org/wiki/Cache_stampede#Cache_stampede_mitigation">cache stampede</a>.</p>
<p><code>(swap! atom f)</code> works something like this:</p>
<ol>
<li>Read the value of <code>atom</code>.</li>
<li>Apply <code>f</code> to the value.</li>
<li><a href="https://en.wikipedia.org/wiki/Compare-and-swap">Compare-and-set</a> the new value to <code>atom</code>: if the value of <code>atom</code> is the same
as it was in step 1, update it to the new value. If it has chenged, go to
step 1.</li>
</ol>
<p>In our case, if the cache gets updated while we’re doing a HTTP request in step
2, the request has to be re-done to update the cache – even if the other update
was for another cache key! If you’re processing a lot of requests in parallel, it may take multiple retries
to succesfully update the cache.</p>
<p>We experienced this at work recently. A microservice was calling another
microservice exactly once per incoming request. When we enabled caching for the
requests, implemented like above, the typical rate of requests went down but we
started to see 10x spikes in requests. This is not what you want to see for one
your busiest services.</p>
<p>Luckily there’s a simple solution: wrap the side-effecting call with <code>delay</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">fetch-data</span> <span class="p">[</span><span class="nv">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">new-value</span> <span class="p">(</span><span class="nf">delay</span> <span class="p">(</span><span class="nf">http-get</span> <span class="nv">url</span><span class="p">))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">-> </span><span class="p">(</span><span class="nf">swap!</span> <span class="nv">my-cache</span> <span class="nv">cache/through-cache</span> <span class="nv">url</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">_</span><span class="p">]</span> <span class="o">@</span><span class="nv">new-value</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">cache/lookup</span> <span class="nv">url</span><span class="p">))))</span>
</span></span></code></pre></div><p>The cache update may still take multiple attempts, but the delayed value is
computed at most once.</p>
<p>This looks a bit messy, so let’s use the new <code>clojure.core.cache.wrapped</code>
namespace that was introduced in core.cache 0.8.0 (August 2019). It takes care
of wrapping the cache in an atom and implements the delaying logic and more:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.core.cache.wrapped</span> <span class="ss">:as</span> <span class="nv">cache</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">my-cache</span> <span class="p">(</span><span class="nf">cache/ttl-cache-factory</span> <span class="p">{}</span> <span class="ss">:ttl</span> <span class="mi">60000</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">fetch-data</span> <span class="p">[</span><span class="nv">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">cache/lookup-or-miss</span> <span class="nv">my-cache</span> <span class="nv">url</span> <span class="nv">http-get</span><span class="p">))</span>
</span></span></code></pre></div><p>This is nice, but there’s still room for improvement.</p>
<p>If multiple threads request the same URL at roughly the same time, they all will
do the HTTP request. It would be more efficient if only one of the threads would
do the request and the other would wait for it to finish. You could implement
this yourself by doing some locking… but you could also use <a href="https://github.com/clojure/core.memoize">core.memoize</a>,
which does it for you.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.core.memoize</span> <span class="ss">:as</span> <span class="nv">memo</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">fetch-data</span> <span class="p">(</span><span class="nf">memo/ttl</span> <span class="nv">http-get</span> <span class="ss">:ttl/threshold</span> <span class="mi">60000</span><span class="p">))</span>
</span></span></code></pre></div><p>I guess the moral of the story is that if you use high-quality higher-level
libraries, the authors will have already solved the thorny lower-level problems
for you.</p>
Generating random tokens in Clojure
https://quanttype.net/p/random-tokens-in-clojure/
Sun, 18 Oct 2020 00:00:00 +0000https://quanttype.net/p/random-tokens-in-clojure/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joutsenet_hu_70cffd7f3825ed72.webp" type="image/webp" />
<img src="https://quanttype.net/images/joutsenet.jpg" width="600" height="428.5" loading="lazy" />
</picture>
</figure>
<p>Web applications have a couple of common use cases for random tokens. For
example, e-mail confirmation or password reset e-mails usually have a link that
contains a random token. The same goes for “share this item” links in case the
item does not have a canonical URL.</p>
<p>The token should be:</p>
<ul>
<li><em>Unpredictable.</em> It would be bad if an attacker was able to guess the token
for a password reset e-mail.</li>
<li><em>URL-safe.</em> You’re going to embed it in an URL.</li>
</ul>
<p>How to generate such tokens in Clojure?</p>
<h2 id="random-data">Random data</h2>
<p>An easy and secure way to generate random data on JVM is to use
<a href="https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html"><code>java.security.SecureRandom</code></a>. On UNIX-y operating systems
SecureRandom uses <code>/dev/urandom</code> by default, which is <a href="https://www.2uo.de/myths-about-urandom/">great at least on
Linux</a>.</p>
<p>How much random data do you need? In other words, how long should the token be?</p>
<ul>
<li>Long eough to not run out of tokens. I’m stating the obivous, but if you’re
going to generate 100k 16-bit tokens, you will generate duplicates as <code>2^16 = 65536 < 100000</code>.</li>
<li>Long enough to avoid collisions. Due to <a href="https://en.wikipedia.org/wiki/Birthday_problem#Square_approximation">birthday paradox</a>, you have
approximately 50% chance of generating a duplicate n-bit token after
generating <code>2^(n/2)</code> tokens. If you use 16-bit tokens, this means you have a
significant chance of collisions already after generating <code>2^8 = 256</code> tokens.</li>
</ul>
<p>You will probably store the tokens in a database with a uniqueness constraint.
Having a high chance of collisions will degrade the performance of your systems
because you have to regularly retry the generation. Worse, an attacker can
generate random tokens themself and try them and they will have a high chance
finding one that works.</p>
<p>In 2009, Colin Percival recommended using 256-bit random IDs in his
<a href="https://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html">Cryptographic Right Answers</a>. He wrote:</p>
<blockquote>
<p>I doubt any application thus far has come close to selecting 2^64 random
values; but if computers continue to scale exponentially, this could occur in
the upcoming decade. In most applications, using 256-bit random values
instead of 128-bit random values carries no significant increase in cost; but
it puts randomly finding a collision safely into the realm of “not going to
happen with all the computers on Earth in the lifetime of the solar system”
problems.</p>
</blockquote>
<p>This seems like good advice<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and Latacora concurred in 2018 <a href="https://latacora.singles/2018/04/03/cryptographic-right-answers.html">in their version of
Cryptographic Right Answers</a>). Go for 256 bits (32 bytes).</p>
<h2 id="url-safety">URL-safety</h2>
<p>By URL-safe, I mean that you should be able to embed the token into an URL and
it should come out intact after all the encoding and decoding and parsing
involved in handling URLs. My favorite answer for making data URL-safe is using
the <a href="https://tools.ietf.org/html/rfc4648#section-5">URL-safe variant of Base64 encoding</a> without padding. It
encodes arbitrary byte data using lower-case and upper-case letters, numbers,
<code>-</code> (minus), and <code>_</code> (underscore). Conveniently Java comes with
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html"><code>java.util.Base64</code></a>.</p>
<h2 id="lets-put-it-all-together">Let’s put it all together</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">import </span><span class="ss">'java.security.SecureRandom</span> <span class="ss">'java.util.Base64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">random</span> <span class="p">(</span><span class="nf">SecureRandom.</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">base64</span> <span class="p">(</span><span class="nf">.withoutPadding</span> <span class="p">(</span><span class="nf">Base64/getUrlEncoder</span><span class="p">))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kd">defn </span><span class="nv">generate-token</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">buffer</span> <span class="p">(</span><span class="nf">byte-array</span> <span class="mi">32</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.nextBytes</span> <span class="nv">random</span> <span class="nv">buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.encodeToString</span> <span class="nv">base64</span> <span class="nv">buffer</span><span class="p">))))</span>
</span></span></code></pre></div><p>Calling <code>(generate-token)</code> returns tokens like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="s">"EE_jyfwk78cQgCcXkO8CAslDhZOL9T-8v9tHXLadenk"</span>
</span></span><span class="line"><span class="cl"><span class="s">"ANB9bv2D_jhYZJVoYk0NQvXNSWrrWisKEGEUdeuosIo"</span>
</span></span><span class="line"><span class="cl"><span class="s">"72mAcjEXWUALSxdmXc0A4jwd51s8t6r-JMmWkFdW868"</span>
</span></span><span class="line"><span class="cl"><span class="s">"Z3ek1rKEJLexyqx9rwZAmIXEBHphRBFLIK5I1zBhC3s"</span>
</span></span><span class="line"><span class="cl"><span class="s">"pknMoF8qZFNsq8nu-8Zfv5WOlaejEkvTM2xxSV6tSis"</span>
</span></span></code></pre></div><p>For something like URL shortener, you may want something shorter and without
ambiguous character combinations like <code>iI1l</code> or <code>oO0Q</code>. Otherwise this should be
a good starting point for your random token needs.</p>
<p><strong>Update:</strong> If you want to make your tokens even more secure, take a moment to
learn about <a href="https://quanttype.net/posts/2021-09-04-split-tokens.html">split tokens</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Counterpoint: Many programming languages and databases have built-in
support for random UUIDs (128 bits, out of which 122 are random), but they
do not have equally convenient way of handling 256-bit IDs. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
clj-branca: lessons learned
https://quanttype.net/p/clj-branca-lessons-learned/
Sun, 11 Oct 2020 00:00:00 +0000https://quanttype.net/p/clj-branca-lessons-learned/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rotten_hu_7289c9208db0f16a.webp" type="image/webp" />
<img src="https://quanttype.net/images/rotten.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Last week, I wrote about about <a href="https://quanttype.net/posts/2020-10-04-branca-and-yak-shaving.html">creating a Clojure library for encoding and
decoding Branca tokens</a>. The library is <em>finally</em> ready. It’s
called <a href="https://sr.ht/~miikka/clj-branca">clj-branca</a> and the version 0.1.1 is now out.</p>
<h2 id="quick-pitch">Quick pitch</h2>
<p>Need to pass information from a service to another service, possibly going
through an user’s browser? URL-safe authenticated encrypted tokens could be the
solution you’re looking for. <a href="https://sr.ht/~miikka/clj-branca">Check clj-branca out!</a></p>
<p>(Please do not use it for stateless sessions, <a href="http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/">it’s a bad idea</a>.
Also, this is a side project, it has not been audited, and I’m not a security
engineer. These same caveats apply to a lot of security-related open-source
libraries but it does not mean that you should ignore them.)</p>
<h2 id="lessons-learned">Lessons learned</h2>
<p>When I started the project, I thought it would be a quick way to kick the tires
of Branca and libsodium. Like I wrote <a href="https://quanttype.net/posts/2020-10-04-branca-and-yak-shaving.html">last time</a>, it wasn’t quick,
but on the other hand, I learned more than I expected.</p>
<p><strong>base62 encoding.</strong> Branca tokens are Base62-encoded. This means taking the raw
token data as an array of bytes and encoding it into printable, URL-safe ASCII
string with a 62 character set. It’s the same idea as Base64, but without the
<code>-</code> and <code>_</code> character used by the URL-safe variant of Base64.</p>
<p>Encoding and decoding Base64 is very efficient. Since <code>log2(64) = 6</code>, each
Base64 character represents exactly six bits. It’s straightforward to create
encoders and decoders that work in linear <code>O(n)</code> time. However, <code>log2(62) ≈ 5.95</code>. Arbitrary radix conversions cannot be done in linear time – based on some
Internet searches, I think their complexity is <code>O(n log(n))</code>. This means Base62
encoding and decoding cannot work in linear time.</p>
<p>Base62 is probably fast enough for all Branca use, but aesthetically this
bothers me. Base64 is practically as URL-safe (as long as you use the URL-safe
variant and do not use padding) and there are performant implementations
available for almost any platform imaginable.</p>
<p><strong>sodium on JVM.</strong> <a href="https://github.com/terl/lazysodium-java">Lazysodium</a> is probably the easiest way to use libsodium on
JVM, because it bundles the libsodium binaries in the jar. However, its “Lazy”
interface does weird hex string conversions and I couldn’t figure out how to use
it interoperably with libsodium on Node.js. I recommend using the “Native”
interface, which looks very C-like, but which does what you expect from the type
signature and libsodium documentation.</p>
<p>Even better option would be to use Google’s <a href="https://github.com/google/tink">Tink</a>, which contains pure-Java
implementations of many of the same algorithms. It encourages you to buy into
its key management scheme, which is probably a good idea.</p>
<p><strong>deps.edn and releases.</strong> Clojure CLI does not come with any tools for building
jars or deploying them to Clojars. <a href="https://juxt.pro/blog/pack-maven">Using this guide</a>, I managed to
cobble together Maven, <a href="https://github.com/juxt/pack.alpha">pack</a>, and a bunch of shell scripts. If you know what
you’re doing, it is possible to create a release with correct SCM (git revision)
information with this setup. The jar is not signed, but <a href="https://quanttype.net/posts/2020-07-26-signing-jars-is-worthless.html">I’ve given up on
that</a>.</p>
<p>Anyway, I would not recommend this brittle setup. If you’re developing new
libraries, save yourself time and energy by using Leiningen and <code>lein release</code>.</p>
<p><strong>sourcehut</strong>. clj-branca is hosted on <a href="https://sourcehut.org/">sourcehut</a>, which is this new software
development platform akin to GitHub. It’s structured as a bunch of separate
services: there’s the <a href="https://sr.ht/~miikka/clj-branca">project hub</a>, <a href="https://git.sr.ht/~miikka/clj-branca">git repo</a>, <a href="https://todo.sr.ht/~miikka/clj-branca">issue
tracker</a>, and, uh, <a href="https://lists.sr.ht/~miikka/public-inbox">mailing list</a>. If you want to
contribute a patch, you’re welcome to send it to the mailing list. You can do
this via sourcehut’s user interface if you register a user account.</p>
<p>To be honest, I’m not a big believer in mailing lists. This is going to be a
barrier against contributions, but I was not expecting many contributions in the
first place.</p>
<p>Sourcehut is pretty basic, but the features that are there seem to work well and
quickly. I think I will use it for my private projects, but the biggest benefit
of GitHub for open-source projects is that everybody else is already an user and
that is going to hard to beat.</p>
Branca and yak shaving
https://quanttype.net/p/branca-and-yak-shaving/
Sun, 04 Oct 2020 00:00:00 +0000https://quanttype.net/p/branca-and-yak-shaving/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/parapluie_hu_7c8d9d76a29598e8.webp" type="image/webp" />
<img src="https://quanttype.net/images/parapluie.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>A while ago I wrote about <a href="https://quanttype.net/posts/2020-08-30-jwt-tokens.html">the alternatives to JWT tokens</a>. One of the them
was <a href="https://branca.io/">Branca</a>. It’s a base62-encoded, XChaCha20-Poly1305 encrypted token with an
abritrary payload. Before writing the post, I thought out it would be nice to
have a Branca library for Clojure and that’s something I’ve occassionally worked
on since then.</p>
<p>First I wrote base62 encoder and decoder and published them as a tiny library
called <a href="https://git.sr.ht/~miikka/clj-base62">clj-base62</a>. That was easy enough. Then I needed to implement the
encryption part.</p>
<p>First I looked at using <a href="https://github.com/lvh/caesium">caesium</a>, a Clojure binding for <a href="https://github.com/jedisct1/libsodium">libsodium</a>. It looked
great except for one thing: it does not come with native libsodium binaries (the
<code>.so</code>/<code>.dll</code>/<code>.dylib</code> files). You’ll have to install them yourself and this
makes using it a hurdle.</p>
<p>Then I looked at using Google’s <a href="https://github.com/google/tink">Tink</a> which has pure-Java implementation of the
required algorithms. It is also <em>opinionated</em> about key management. The opinions
seem very smart, but it also makes it hard to just pass in a string as an
encryption key. Passing in a string seems like not so great idea, but I’d like
my library to be interoperable with the other Branca libraries that do exactly
that.</p>
<p>I also couldn’t build <code>tinkey</code>, Tink’s key management tool, on my personal
laptop. It requires a specific version of Bazel that was not available via
NixOS’s package collection.</p>
<p>Finally I tried using <a href="https://github.com/terl/lazysodium-java">Lazysodium</a>. Like caesium, it’s a Java binding for
libsodium, but unlike caesium, it comes with the binaries. I quickly implemented
token encryption and decryption - and realized that my code refuses to decode
tokens produced by <a href="https://github.com/tuupola/branca-js">branca-js</a> and vice versa.</p>
<p>I was using Lazysodium’s Lazy interface. It operates on strings, which seemed
weird to me, until I realized that it’s not just byte arrays disguised as
strings – it’s hex strings (strings of ASCII hexademical digits). So now I
still need to either add some byte-arrays-to-hex-strings conversions or use
Lazysodium’s Native interface, which is very C-like but which operates on byte
arrays. Then I’m hopefully done.</p>
<p>I thought implementing Branca tokens in Clojure would be a quick two-evening
job. Alas.</p>
Solving the diamond problem with shading
https://quanttype.net/p/solving-diamond-problem-with-shading/
Sun, 27 Sep 2020 00:00:00 +0000https://quanttype.net/p/solving-diamond-problem-with-shading/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/koira_hu_91f14f3832c8fe7.webp" type="image/webp" />
<img src="https://quanttype.net/images/koira.jpg" width="600" height="450" alt="A close up of the head of a weiner dog sculpture. The dog looks intense." loading="lazy" />
</picture>
</figure>
<p>Dealing with difficult library upgrades has been a recurring task in my career
as a software developer. This week I got a new tool into my toolbox for handling
dependency conflicts.</p>
<p>I was working on a Scala project. It heavily uses a library that is no
longer maintained. We would like to migrate to a newer library.</p>
<p>It would nice to migrate piece-by-piece: that would make both development and
testing much easier. However, both the libaries depend on the different version
of the same library. Our dependency graph looks something like this:</p>
<figure>
<picture>
<img src="https://quanttype.net/p/solving-diamond-problem-with-shading/diamond1.svg"
/>
</picture>
</figure>
<p>In our case, there’s no need to pass data structures between <code>old-lib</code> and
<code>new-lib</code>, so this <em>could</em> work if v1 and v2 were somewhat compatible.
Unfortunately that was not the case: the changes were so severe that our app
would not even start if the newer version of <code>base-lib</code> was added to the
classpath.</p>
<p>This is a variation of the <a href="https://en.wikipedia.org/wiki/Dependency_hell#Problems">diamond dependency problem</a>. It’s an
annoying problem in general, but in this case we were able to solve it easily by
<a href="https://softwareengineering.stackexchange.com/a/351091">shading</a> <code>base-lib</code> in <code>old-lib</code>. This means that we created our own version of
<code>old-lib</code> that bundles <code>base-lib</code> with all the <code>base-lib</code> classes renamed
and all the usage sites in <code>old-lib</code> changed to use the new names.</p>
<p>For example, <code>net/quanttype/base_lib/ExampleClass.class</code> would be renamed to
<code>shaded/net/quanttype/base_lib/ExampleClass.class</code> in the resulting <code>.jar</code> file.
Our dependency graph now looks like this:</p>
<figure>
<picture>
<img src="https://quanttype.net/p/solving-diamond-problem-with-shading/diamond2.svg"
/>
</picture>
</figure>
<p>Note that <code>shaded-old-lib</code> does not explicitly depend on <code>old-lib</code> or
<code>base-lib</code>, since it already includes shaded versions of them.</p>
<p>Shading sounds difficult, but it was easy in practice using <a href="https://github.com/sbt/sbt-assembly">sbt-assembly</a>’s
built-in <a href="https://github.com/sbt/sbt-assembly#shading">shading support</a>. To do this, we need to create two sbt
projects. The first one is the one that does shading:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scala" data-lang="scala"><span class="line"><span class="cl"><span class="k">lazy</span> <span class="k">val</span> <span class="n">shadedOldLibRoot</span> <span class="k">=</span> <span class="n">project</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="n">settings</span><span class="o">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">libraryDependencies</span> <span class="o">++=</span> <span class="nc">Seq</span><span class="o">(</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// We depend on old-lib, which pulls in the correct version of base-lib
</span></span></span><span class="line"><span class="cl"> <span class="s">"net.quanttype"</span> <span class="o">%%</span> <span class="s">"old-lib"</span> <span class="o">%</span> <span class="s">"1.0.0"</span>
</span></span><span class="line"><span class="cl"> <span class="o">),</span>
</span></span><span class="line"><span class="cl"> <span class="n">assemblyShadeRules</span> <span class="n">in</span> <span class="n">assembly</span> <span class="k">:</span><span class="o">=</span> <span class="nc">Seq</span><span class="o">(</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// You can add more rules here if needed
</span></span></span><span class="line"><span class="cl"> <span class="nc">ShadeRule</span><span class="o">.</span><span class="n">rename</span><span class="o">(</span><span class="s">"net.quanttype.base_lib.**"</span> <span class="o">-></span> <span class="s">"shaded.net.quanttype.base_lib.@1"</span><span class="o">).</span><span class="n">inAll</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="o">),</span>
</span></span><span class="line"><span class="cl"> <span class="n">assemblyOption</span> <span class="n">in</span> <span class="n">assembly</span> <span class="k">:</span><span class="o">=</span> <span class="o">(</span><span class="n">assemblyOption</span> <span class="n">in</span> <span class="n">assembly</span><span class="o">).</span><span class="n">value</span><span class="o">.</span><span class="n">copy</span><span class="o">(</span><span class="n">includeScala</span> <span class="k">=</span> <span class="kc">false</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"> <span class="n">skip</span> <span class="n">in</span> <span class="n">publish</span> <span class="k">:</span><span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="o">)</span>
</span></span></code></pre></div><p>The second project publishes the first one without the dependency information:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scala" data-lang="scala"><span class="line"><span class="cl"><span class="k">lazy</span> <span class="k">val</span> <span class="n">shadedOldLib</span> <span class="k">=</span> <span class="n">project</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="n">settings</span><span class="o">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="k">:</span><span class="o">=</span> <span class="s">"shaded-old-lib"</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="n">in</span> <span class="nc">ThisBuild</span> <span class="k">:</span><span class="o">=</span> <span class="s">"1.0.0"</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">organization</span> <span class="n">in</span> <span class="nc">ThisBuild</span> <span class="k">:</span><span class="o">=</span> <span class="s">"net.quanttype"</span>
</span></span><span class="line"><span class="cl"> <span class="n">packageBin</span> <span class="n">in</span> <span class="nc">Compile</span> <span class="k">:</span><span class="o">=</span> <span class="o">(</span><span class="n">assembly</span> <span class="n">in</span> <span class="o">(</span><span class="n">shadedOldLibRoot</span><span class="o">,</span> <span class="nc">Compile</span><span class="o">)).</span><span class="n">value</span>
</span></span><span class="line"><span class="cl"> <span class="o">)</span>
</span></span></code></pre></div><p>The resulting package can be installed to the local Ivy repository with <code>sbt shadedOldLib/publishLocal</code>. You can do full-fledged <code>publish</code> if you want – for
us the local version was enough.</p>
<p>Add the new package to the dependencies of your main project:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scala" data-lang="scala"><span class="line"><span class="cl"><span class="n">libraryDependencies</span> <span class="o">+=</span> <span class="s">"net.quanttype"</span> <span class="o">%%</span> <span class="s">"shaded-old-lib"</span> <span class="o">%</span> <span class="s">"1.0.0"</span>
</span></span></code></pre></div><p>Finally, if you have used <code>base-lib</code> directly in your app, you’ll have rewrite
any imports in your codebase. Like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">find . -name <span class="s2">"*.scala*"</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"> xargs sed -i .bak <span class="s1">'s/net\.quanttype\.base_lib/shaded.\0/g'</span>
</span></span></code></pre></div><p>Now you can add <code>new-lib</code> as a dependency and everything should just work.</p>
<h2 id="when-to-use-this">When to use this?</h2>
<p>There are some drawbacks to this approach.</p>
<p>The biggest one is that if <code>old-lib</code> or <code>base-lib</code> have any other dependencies,
they get included in <code>shaded-old-lib</code>. If your app also depends on them, there
may be new conflicts and they’re more confusing than before, since the conflict
is not directly visible in the dependency graph. You can manually exclude those
dependencies - see sbt-assembly’s <a href="https://github.com/sbt/sbt-assembly#excluding-jars-and-files">instructions</a>.</p>
<p>For us, shading solved the diamond problem neatly. Since we’re trying to migrate
away from the old library, the drawbacks were acceptable.</p>
Local memoized recursive functions
https://quanttype.net/p/local-memoized-recursive-functions/
Sun, 20 Sep 2020 00:00:00 +0000https://quanttype.net/p/local-memoized-recursive-functions/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kvarken1_hu_76587f5f994ae1bc.webp" type="image/webp" />
<img src="https://quanttype.net/images/kvarken1.jpg" width="600" height="450" alt="A view of a sea shore." loading="lazy" />
</picture>
</figure>
<p>You probably know how to create a top-level memoized recursive function in
Clojure, but how do you create a local one? By local, I mean defined with <code>let</code>
or <code>letfn</code>.</p>
<p>For the lack of better example, consider a Clojure function that returns the
n-th Fibonacci number. Here’s a top-level definition created with <code>defn</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">fibo</span> <span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))</span>
</span></span></code></pre></div><p>It’s a classic example of recursive function. However, the implementation above
is not exactly efficient. If you run <code>(fibo 50)</code>, it will take forever to
finish.</p>
<p>There are two standards ways to make it fast: tail recursion and memoization.
You can use <code>clojure.core/memoize</code> to create a memoized version of <code>fibo</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">fibo2</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">memoize</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo2</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo2</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">fibo2</span> <span class="mi">20</span><span class="p">)</span> <span class="c1">; 6765</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">fibo2</span> <span class="mi">50</span><span class="p">)</span> <span class="c1">; 12586269025</span>
</span></span></code></pre></div><p>This is fast enough for calculating the 50th Fibonacci number.</p>
<p><code>fibo2</code> is defined on the top level, but how do you create a locally-bound
version of it? It seems like it shouldn’t be too hard. Here’s the non-memoized
version created with <code>let</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="k">fn </span><span class="nv">fibo</span> <span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo</span> <span class="mi">20</span><span class="p">))</span>
</span></span></code></pre></div><p>Note that we had to name the function twice: first in the <code>let</code> form and second
time inside <code>fn</code>. This is because the binding created by <code>let</code> is not visible
inside <code>fn</code>. With <code>letfn</code>, only one name is enough:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">letfn</span> <span class="p">[(</span><span class="nf">fibo</span> <span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo</span> <span class="mi">20</span><span class="p">))</span>
</span></span></code></pre></div><p><strong>Exercise:</strong> If you want, try to define a memoized version of <code>fibo</code> inside
<code>let</code> yourself. Try calling it with large <code>n</code> to check that it works correctly.</p>
<p><em>Here are a couple of nice pictures so that you don’t accidentally scroll to the
answer before you’re ready. If you don’t feel like attempting the exercise,
that’s okay too.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kvarken2_hu_5088f0d16698da32.webp" type="image/webp" />
<img src="https://quanttype.net/images/kvarken2.jpg" width="600" height="450" alt="Waves hitting the rocks at a sea shore. There are rocks and a small island in the background." loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kvarken3_hu_38e5847637622c3.webp" type="image/webp" />
<img src="https://quanttype.net/images/kvarken3.jpg" width="600" height="450" alt="Rocks at the sea shore." loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kvarken4_hu_7dac192a00c5c264.webp" type="image/webp" />
<img src="https://quanttype.net/images/kvarken4.jpg" width="600" height="600" alt="Close-up of berries and leaves of sea buckthorn." loading="lazy" />
</picture>
</figure>
</p>
<p>We can’t use <code>memoize</code> with <code>leftn</code>, so let’s continue with <code>let</code>. Here’s a
naïve attempt:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="nf">memoize</span> <span class="p">(</span><span class="k">fn </span><span class="nv">fibo</span> <span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">))))))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo</span> <span class="mi">20</span><span class="p">))</span>
</span></span></code></pre></div><p>If you try this with <code>(fibo 50)</code>, you’ll notice that it does not work as
intended. This is because inside the <code>fn</code>, <code>fibo</code> refers to the <code>fn</code> itself and
not to the memoize-wrapped version. If you called <code>(fibo 50)</code> twice in a row,
the second time would be fast since the final result of the calculation does get
memoized.</p>
<p>There are <a href="https://stackoverflow.com/a/3908020">a bunch of solutions</a> on StackOverflow. For example, the answer
by Michał Marczyk can be adopted:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">with-local-vars</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="nf">memoize</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">))))))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.bindRoot</span> <span class="nv">fibo</span> <span class="o">@</span><span class="nv">fibo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">@</span><span class="nv">fibo</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo</span> <span class="mi">50</span><span class="p">))</span>
</span></span></code></pre></div><p>However, this code probably makes you go <em>hmmm</em>. Calling Var methods via Java
interop does not feel like very Clojure-like to me. I want to offer an
alternative, more <em>functional</em> solution.</p>
<p>The function can’t refer to the memoized version of itself from the inside, but
we can pass it in from the outside as a parameter. Let’s first try it without
memoization:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">rec</span> <span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">rec</span> <span class="nv">rec</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">rec</span> <span class="nv">rec</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo</span> <span class="nv">fibo</span> <span class="mi">40</span><span class="p">))</span>
</span></span></code></pre></div><p>The first parameter of <code>fibo</code> is the function itself. It looks a bit odd, but it
works! We can, <a href="https://quanttype.net/posts/2016-03-29-defaultdicts-all-the-way-down.html">once again</a>, clean it up by using a fixed-point
combinator. It allows us to wrap <code>fibo</code> so that it receives the wrapped version
of itself as the first argument.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">fix</span> <span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="k">fn </span><span class="nv">g</span> <span class="p">[</span><span class="o">&</span> <span class="nv">args</span><span class="p">]</span> <span class="p">(</span><span class="nb">apply </span><span class="nv">f</span> <span class="nv">g</span> <span class="nv">args</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">fibo</span> <span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl"> <span class="nv">fibo2</span> <span class="p">(</span><span class="nf">fix</span> <span class="nv">fibo</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo2</span> <span class="mi">20</span><span class="p">))</span>
</span></span></code></pre></div><p>Now the function definition looks pretty normal. <code>fibo</code> takes “itself” as the
first parameter, but it’s not very different from <code>(fn fibo [n] ...)</code>. Let’s try
to memoize it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">fibo</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">fibo</span> <span class="nv">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">+ </span><span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">dec </span><span class="nv">n</span><span class="p">))</span> <span class="p">(</span><span class="nf">fibo</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">2</span><span class="p">)))))</span>
</span></span><span class="line"><span class="cl"> <span class="nv">fibo2</span> <span class="p">(</span><span class="nf">fix</span> <span class="p">(</span><span class="nf">memoize</span> <span class="nv">fibo</span><span class="p">))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">fibo2</span> <span class="mi">50</span><span class="p">))</span>
</span></span></code></pre></div><p>It works!</p>
<p>I’m always delighted to find use cases for fixed-point combinators. Writing a
nice macro to wrap this up is left as an exercise for the reader.</p>
No blog this week
https://quanttype.net/p/no-blog-this-week/
Sun, 13 Sep 2020 00:00:00 +0000https://quanttype.net/p/no-blog-this-week/<p>I’ve committed to blogging every week and I have even a Beeminder goal to
enforce it. But really, there’s no blog post in me this week, so I’m just
posting this to appease Beeminder. Sorry.</p>
Is this art?
https://quanttype.net/p/is-this-art/
Sun, 06 Sep 2020 00:00:00 +0000https://quanttype.net/p/is-this-art/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/saari_hu_f654ed49f8d4f86f.webp" type="image/webp" />
<img src="https://quanttype.net/images/saari.jpg" width="600" height="450" alt="A view from a hill on an island. There's rock and trees in the front and forest-covered small island in the sea in the back." loading="lazy" />
<figcaption>This view in Pensar reminds me of certain famous Finnish paintings.</figcaption>
</picture>
</figure>
<p>Sometimes when people encounter a creative work that challenges them, they ask
<em>“is this art?”</em> and struggle to answer. To find a satisfying answer, you need
to go beyond that question.</p>
<p><em>Epistemic status</em>: Armchair philosophy.</p>
<p>I’m not going to give an all-encompassing definition of <em>art</em>, but the word is
used in at least two senses:</p>
<ol>
<li>Art as in artwork: Creative works made with the intention of expressing
ideas and skill and affecting emotions.</li>
<li>Art as in artful: Works that are skillful and aesthetic.</li>
</ol>
<p>When somebody says that <em>“this piece of code is a work of art”</em>, they usually
mean the latter (<a href="https://code-art.xyz">there are exceptions</a>). When you refer to the body of
work by an artist, you mean the former, although the latter might apply as well.
The trouble starts when you conflate these two meanings.</p>
<p>Some famous (if dated) works that raise the question of what is art are Marcel
Duchamp’s <em>Fountain</em> and John Cage’s <em>4'33"</em>. That is just as well – they were
meant to do that. They both are art in the former sense, but are they skillful?
Considering their fame, I’d say yes, but the skills they demonstrate are
different from the usual kind of sculpture and composition.</p>
<p>The word <em>art</em> is too loaded and leads to frustrating discussions. If a creative
work has been created with <em>artistic intention</em>, why not just accept that it is
art or at least analyze like it was art? Then you can continue to more
interesting questions to poke at its artistic merits. For example:</p>
<ul>
<li>Does it evoke emotions?</li>
<li>Does it convey a message?</li>
<li>Does it interest you?</li>
<li>Is it boring, or imaginative, or beautiful, or hidesome, or funny?</li>
<li>Was it made skillfully?</li>
<li>Why did the author make it?</li>
<li>Why was it exhibited or performed?</li>
</ul>
JWT and its alternatives
https://quanttype.net/p/jwt-tokens/
Sun, 30 Aug 2020 00:00:00 +0000https://quanttype.net/p/jwt-tokens/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kivet_hu_67cf10496213a31c.webp" type="image/webp" />
<img src="https://quanttype.net/images/kivet.jpg" width="600" height="400" alt="A picture of rocky shore from the above. The rocks grow green algae." loading="lazy" />
</picture>
</figure>
<p>So are there any worthwhile alternatives to JWTs?</p>
<p>JSON Web Tokens (JWT) are a popular way to create URL-safe access tokens for web
applications. They are often used for stateless sessions<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and they’re
part of OpenID Connect (OIDC) protocol.</p>
<p><em>Note: JWT is just one part of the JavaScript Object Signing and Encryption
(JOSE) framework. It would be more accurate to talk about JOSE but JWT has come
to represent the whole suite, so I’ll go with that.</em></p>
<p>Unfortunately, JWT is not a great design. It’s complex and many implementations
have had serious security flaws directly connected to that complexity. For
example: <a href="https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid">No Way JOSE! Javascript Object Signing and Encryption is a Bad
Standard That Everyone Should Avoid</a></p>
<p>Recently I had a legitimate need for a URL-safe authenticated token. I needed to
pass some information via the user’s browser from one service to another one
that I control. The receiving service had to be verify that the information was
coming from the original service intact.</p>
<p>The common wisdom on Twitter is that just slap a HMAC-SHA256 on it. That seems
like a good idea. To avoid any issues with canonicalization, I’d dump my data as
JSON, encode the result with base64url (no padding!), and authenticate that with
HMAC-SHA256 and a secret shared by the services.</p>
<p>This sounds very much like a JWS (the signed variant of JWT). I’d need to
implement it in two programming languages, JavaScript and Clojure. That sounded
like a chore and the temptation to just a library was weighing heavy on me. But
since JWTs are <em>bad</em>, I decided to take a look at alternatives.</p>
<p>I know of a couple of alternatives to JWTs:</p>
<p><strong><a href="https://github.com/fernet/spec">Fernet</a></strong>. Encrypted tokens only. Encrypted with AES-128 in CBC mode and
encoded with base64url. The choice of cryptographic algorithm seems a bit dated
by now. Simple.</p>
<p><strong><a href="https://branca.io/">Branca</a></strong>. Encrypted tokens only. Encrypted with XChaCha20-Poly1305 and encoded
with base62. Intended as a modern version of Fernet. Simple, but base62 seems
like an odd choice when base64url implementations are so widely available.</p>
<p><strong><a href="https://github.com/paragonie/paseto">PASETO</a></strong>. Has both signed and encrypted variants. Version 2 (the latest
version) uses XChaCha20-Poly1305 with base64url encoding. More complex and
e.g. has its own string array encoding. Still less complex than JWT.</p>
<p>I don’t know how both Branca and PASETO both ended up with XChaCha20-Poly1305.
It’s not something that I’m familiar with and I don’t follow the latest
developments in cryptography, but I’m going to assume that it’s a good, modern
choice. At least it’s available in libsodium.</p>
<p>Any of these tokens standards could have solved my problem. I didn’t <em>need</em>
encryption but I don’t mind it as long as the tokens are authenticated.</p>
<p>Nevertheless, I decided to go with JWTs. I took a look at the library situation
of the alternative tokens and either the libraries did not exist or they looked
like they hadn’t seen much use.</p>
<p>Working with OIDC, I’ve had to deal with JWTs for years, and by this point there
are libraries that I trust despite the abysmal track record of JWT libraries in
general (shoutout to <a href="https://github.com/funcool/buddy">Buddy</a> for Clojure!). There are also tools such as
<a href="https://jwt.io/">JWT Debugger</a> and <a href="https://smallstep.com/cli/">step</a> command-line tool. Creating a library and tooling
myself would be possible, but not a worthwhile investment for an one-off
library.</p>
<p>Looks like I’m going to be stuck with JWTs for a while.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Stateless meaning that there’s no server-side state. The session
state lives in the token. It sounds great on paper, but in my experience
you’ll end up introducing server-side state by the time you try to make
<a href="https://quanttype.net/posts/2020-08-08-logging-out-is-hard.html">logout</a> work the way you want. For more information, see
<a href="http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/">Stop using JWT for sessions, part 1</a> and <a href="http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/">part 2</a>
by Sven Slootweg. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Looking good in a suit
https://quanttype.net/p/looking-good-in-clothes/
Sun, 23 Aug 2020 00:00:00 +0000https://quanttype.net/p/looking-good-in-clothes/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/horse_hu_746e72865c906e8a.webp" type="image/webp" />
<img src="https://quanttype.net/images/horse.jpg" width="600" height="480" alt="A white horse standing in a meadow. There are a lot of rocks in the background." loading="lazy" />
</picture>
</figure>
<p>What have you been wearing during COVID-19?</p>
<p>For me, the strong isolation measures meant that I started working remotely. I
wasn’t meeting people and this changed how I dressed. Instead of my normal
office clothes - sweaters and chinos - I would dress in athleisure. Basically I
looked all the time like I was on my way to a yoga session or a hike.</p>
<p>Recently, however, I’ve had the chance to wear a suit. We’ve celebrated some big
events in friends’ lives.</p>
<p>Picking the right clothes was a bit of problem. I have this early 2000s (almost
vintage!) grey suit that I really like. It has <a href="https://en.wikipedia.org/wiki/Glen_plaid">Prince of Wales check</a> and
wide legs. Unfortunately the fit is not perfect: it bunches slightly it the
neck.</p>
<p>I’ve have these three commandments for looking good in clothes:</p>
<ol>
<li>The clothes should be clean and not damaged.</li>
<li>The clothes should fit you well.</li>
<li>The clothes should exhibit good taste.</li>
</ol>
<p>The earlier commandments are more important than the later ones. No matter how
fancy clothes, they won’t look good on you if they’re of wrong size or stained.</p>
<p>I nevertheless ended up choosing the grey suit, because between it and the black
suit that does not meet the first commandment, it was the better choice…</p>
The minimalist program
https://quanttype.net/p/the-minimalist-program/
Sun, 16 Aug 2020 00:00:00 +0000https://quanttype.net/p/the-minimalist-program/<p>In visual arts, minimalism was a movement that emphasized non-figurative,
non-emotive, abstract art. There’s no need to refer to the world – the shapes
and the materials make the piece of art interesting in itself.</p>
<p>In computing, minimalism refers to something else. Mostly it’s about reduction
and parsimony. But I wonder: are there programs that would fit the visual arts
definition of minimalism?</p>
<p>The programs relate to the world by solving problems. The minimalist program
should be afunctional. It should not solve problem – it should exist because the
computation itself is worth of our attention.</p>
<p>Can you think of such programs? My suggestion would be <a href="https://en.wikipedia.org/wiki/Quine_(computing)">quine</a>, a program
that prints its own source code.</p>
Two things that make logging out hard
https://quanttype.net/p/logging-out-is-hard/
Sat, 08 Aug 2020 00:00:00 +0000https://quanttype.net/p/logging-out-is-hard/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/newrock_hu_449e9889a3f7637d.webp" type="image/webp" />
<img src="https://quanttype.net/images/newrock.jpg" width="600" height="480" alt="The shadow of the author cast over a rock in the shore. There's some yellow lichen on the rock." loading="lazy" />
</picture>
</figure>
<p>When building a web service, logging out is often an afterthought. When you
finally get to it, it turns out to be complicated.</p>
<p>As a user, you have a couple of reasons to log out from a web service:</p>
<ul>
<li>You want to switch to another account.</li>
<li>You want to prevent anyone else (or even yourself) from accessing your account using the same device.</li>
</ul>
<p>We now have multiple devices and the services can be used with browsers and
native apps. Sometimes you want to log out of the <em>other</em> devices, for example if
your phone gets lost or stolen. The service administrator may log out all your
devices if they detect that your account has been compromised<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>In a traditional Web 2.0 architecture, the active sessions are stored in a
database. The user gets a HTTP cookie with the session ID and every request is
checked against the database to see if the session is still active and to whom
it belongs. Implementing log out is straightforward: delete the user’s session
from the database and the user is logged out.</p>
<p>Contemporary web services and single-page applications come with two things that
complicate the situation:</p>
<p><strong>Single sign-on (SSO).</strong> You have logged in to multiple services using the same
credentials. Now you click the log out button in one of the services. What
happens? Do you get logged out from that service or all the services? On that
device or on all the devices? Does the answer depend on whether the single
sign-on provider is run by the same organization as the service or by a third
party?</p>
<p>I’ve discussed this question with developers, designers, and product managers
and I’ve learned that very reasonable people disagree about it. <em>It depends.</em></p>
<p><strong>Stateless sessions.</strong> Instead of storing sessions in a database, you can use a
signed (and possibly encrypted) cookie or a token. As long as the user’s
requests include a valid token, they’re logged in. There’s no server-side state
associated with the session, hence the name.</p>
<p>In this design, logging out means forgetting the session token. You can unset
the cookie and delete the tokens from applications’ storage. The security people
do not like this, because the token stays valid even if the user has nominally
logged out. Having a short expiration time helps, but then you have to figure
out how to periodically get fresh tokens. Logging out other devices is
impossible without re-introducing server-side state.</p>
<p><strong>In conclusion.</strong> If you’re looking to implement a SSO system or stateless
session architecture, talk about how logging out should work early on.
Especially do this if you’re going to use OpenID Connect (which often is used in
stateless fashion). It does not come with great answers to these questions.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>For example, if they detect that <em>all</em> the accounts on their service have been compromised. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Summer reading
https://quanttype.net/p/a-couple-of-books/
Sun, 02 Aug 2020 00:00:00 +0000https://quanttype.net/p/a-couple-of-books/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/shore_hu_28eb5eb49e332e0c.webp" type="image/webp" />
<img src="https://quanttype.net/images/shore.jpg" width="600" height="600" alt="Moon shining over the sea in the evening. There's a rocky shore close to the camera and small islands in the horizon." loading="lazy" />
</picture>
</figure>
<p>In case you’re looking for some summer reading, here are a couple of books that
I’ve read this year and want to recommend:</p>
<p><strong>This Is How You Lose the Time War</strong> by <em>Amal El-Mohtar</em> and <em>Max Gladstone</em>.
It’s a story about two time agents who fight on the opposing sides but slowly
they get to know each other. The story is told entirely through the letters they
send to each other. The book is rich in language and references.</p>
<p>I recommend it to sci-fi readers, but I think that even people who are not into
sci-fi could like it if you’re into <em>literature</em>.</p>
<p><strong>Bad Blood</strong> by <em>John Carreyrou</em>. The book tells the story of the infamous
Silicon Valley startup Theranos. To me, it’s a story of the magic of Silicon
Valley: you can will almost anything to existence if you are just persistent
enough, and have the right connections and look the part. It’s rather
thrilling!</p>
<p>I recommend it to people who follow Silicon Valley startup news. (If you’re a
programmer and you read Hacker News, that counts.)</p>
<p><strong>Monimuotoisuus</strong> by <em>Juha Kauppinen</em>. This one is in Finnish. It’s about the
decline of diversity in the Finnish nature. The author visits a number of places
in Finland, looking for species that are threatened or already extinct. These
eloquent stories are used to explain the importance of diverse habitats. It’s a
sad book.</p>
<p>I’d recommend it to people who enjoy the nature, and can read in Finnish, of
course.</p>
Signing .jars is worthless
https://quanttype.net/p/signing-jars-is-worthless/
Sun, 26 Jul 2020 00:00:00 +0000https://quanttype.net/p/signing-jars-is-worthless/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/path_hu_f082a68dfef99588.webp" type="image/webp" />
<img src="https://quanttype.net/images/path.jpg" width="600" height="400" alt="Duckboards make a serpentine curve over a marsh." loading="lazy" />
</picture>
</figure>
<p>If you try to deploy a new release of Clojure library with
<a href="https://leiningen.org/">Leiningen</a>, it prompts you to sign the .jar file with GPG. This step
often causes confusion and breaks. I believe that it’s not worth the effort to
make it work.</p>
<p>As far as I know, <em>nobody ever verifies the signatures</em> in a systematic way.
There are a bunch of obstacles:</p>
<ul>
<li>It’s unclear if any tools for verifying the signatures actually work. For
example, I just tried to run <code>lein deps :verify</code> against a couple of projects
and it reported every dependency as <code>:unsigned</code>. I know that some of those
dependencies are signed and I verified that the <code>.asc</code> files exist on
repo.clojars.org.</li>
<li>It’s hard to find the public keys for the library maintainers. Sometimes they
upload them on the keyservers, sometimes not.</li>
<li>There’s no established way of communicating that which public keys should be
trusted. If there’s a new release and it has been made with a new key, your
best bet is to e-mail the maintainer and ask what is up.</li>
</ul>
<p>It’s hard to get any security benefits from the signatures in practice. Thus
it’s okay to set <a href="https://github.com/technomancy/leiningen/blob/998d373ae06d17234efffde761fae93242c736fa/sample.project.clj#L111"><code>:sign-releases</code></a> to <code>false</code> in your
project.clj even if Leiningen’s manual does not recommend it. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="ss">:deploy-repositories</span> <span class="p">[[</span><span class="s">"clojars"</span> <span class="p">{</span><span class="ss">:url</span> <span class="s">"https://clojars.org/repo"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:sign-releases</span> <span class="nv">false</span><span class="p">}]]</span>
</span></span></code></pre></div><hr>
<p>In principle, the systematic checking of signatures could provide security
against a dangerous supply-chain attack: weak or leaked passwords for package
manager accounts. For example, <a href="https://arstechnica.com/information-technology/2019/08/the-year-long-rash-of-supply-chain-attacks-against-open-source-is-getting-worse/">several RubyGems</a> have been attacked
this way. Most likely the signing keys would not be compromised at the same
time.</p>
<p>There are alternative solutions, though, such as disallowing publishing packages
without multi-factor authentication. Using Clojars’s <a href="https://groups.google.com/forum/#!topic/clojure/GmAU4XwnRpw">deploy tokens</a>
helps a bit as well.</p>
<p>Right now we place a lot of trust on Clojars and Maven Central. If either of
them got compromised, we all would be screwed. Package signing could be a part
of a solution to mitigate that risk, but a comprehensive solution would be
something like using <a href="https://theupdateframework.io/">The Update Framework</a>. Go’s <a href="https://blog.golang.org/module-mirror-launch">checksum
database</a> is also worth taking look at.</p>
<p>Finally, if you’re moved to do something about this, please do not build
anything new using PGP. To quote Latacora: <a href="https://latacora.micro.blog/2019/07/16/the-pgp-problem.html">PGP is bad and needs to go
away</a>.</p>
<hr>
<p>I’ve written this post in part to be proven wrong. I’m eagerly waiting for posts
from y’all about how you do, in fact, systematically verify the signatures.</p>
On paddling
https://quanttype.net/p/on-paddling/
Sun, 19 Jul 2020 00:00:00 +0000https://quanttype.net/p/on-paddling/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/repovesi_hu_1be2d6f1d86c9074.webp" type="image/webp" />
<img src="https://quanttype.net/images/repovesi.jpg" width="600" height="400" alt="Branches lying underwater at a shoreline." loading="lazy" />
</picture>
</figure>
<p>Every now and then, I kayak. I joined a kayaking club for a couple of years ago,
but I’m still a novice. I just haven’t been kayaking that much. This summer I’ve
made a new effort to become more skilled and confident at paddling.</p>
<p>I’ve been practicing the basic techniques - how to paddle efficiently and how to
maneuver the kayak. Turning a kayak is easy enough when the water is flat, but
when there’s wind and waves, it gets more involved.</p>
<p>Just the other day I got in a situation where I was sideways to the wind and
hard time getting the kayak to turn to either direction without using <a href="https://www.kayakpaddling.net/2-3">stern
rudder</a>. It’s weird because usually the kayaks turns into the wind, or if the
skeg is down, downwind. I’m sure the situation would have been trivial for an
experienced kayaker but for me it was a mystery.</p>
<p>What I like about kayaking is how immediate you are with the sea. It’s right
there. The sea is a bit scary! There is always wind and waves, and there are
boats and ships. Before midsummer, the water is cold and now it’s just cool.
But I also like it - it feels good to learn to deal with the waves and
everything.</p>
<p>I’ve come to like paddling alone. Due to my meager skills, I’ve had to limit
myself to safe and familiar routes and calm weather. Once I learn more, I can
explore more alone. There’s a good reason why every paddling safety guide
suggests paddling in a group: capsizes, accidents, and any other problem
situations are much easier to deal with whene there are multiple people.</p>
<p><em>There’s no kayaking photo in this post because, well, I haven’t taken any. At
first I took some photos with my phone - I had this handy transparent waterproof
floating case for my phone. One day I dropped it in the water and turns out it’s
neither waterproof or floating. I managed to catch phone before it sunk, and it
survived intact, but putting it a non-transparent actually-waterproof bag
prevents photography.</em></p>
Summer vacation
https://quanttype.net/p/summer-vacation/
Sun, 14 Jun 2020 00:00:00 +0000https://quanttype.net/p/summer-vacation/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jatski_hu_eb14484db3970bc7.webp" type="image/webp" />
<img src="https://quanttype.net/images/jatski.jpg" width="600" height="600" alt="A portion of green ice cream in a cup." loading="lazy" />
</picture>
</figure>
<p>I’m starting my summer vacation next week and this blog will also take a small
break. My original plan was to hike on Kungsleden in Sweden, but COVID-19 messed
that up. I’m not sure what I’m going to do – I guess I’ll settle for some
hiking and paddling in Southern Finland. I’ll be back in July.</p>
Building software without hiring anyone
https://quanttype.net/p/building-software-without-hiring/
Sun, 07 Jun 2020 00:00:00 +0000https://quanttype.net/p/building-software-without-hiring/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ants3_hu_d439b31465ac5aaf.webp" type="image/webp" />
<img src="https://quanttype.net/images/ants3.jpg" width="600" height="400" alt="Ants crawling over an old, partially-decomposed log." loading="lazy" />
</picture>
</figure>
<p>A number of enterprises in Finland rely heavily on external contractors for
software development even though the software is a core part of their business.
Often the product owners, project managers, and possibly architects work for the
company, but the designers, developers, ops, QA specialists, and data scientists
work for consulting companies.</p>
<p>Contractors are the obvious choice when you need extra staff quickly or when you
need someone with skills that your organization does not have.</p>
<p>For example, many years ago when I worked at ZenRobotics, none of us were
experts in building user interfaces. Our product, the robotic recycler, needed a
control panel, so we hired a consulting company to build it. I wasn’t too happy
with the result back then<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but the premise still makes sense to me.</p>
<p>But why use contractors as the backbone of your software development
organization? I have a couple of educated guesses, but lacking insider
knowledge, I’m going to keep them to myself for now. Instead, I’m going to
highlight a couple of oddities compared to the more traditional solution of
hiring people.</p>
<p><strong>Teams are thrown together.</strong> When the client needs more hands, there’s no
hiring process. Instead, there’s either a sales process or a public tender. The
client has little agency in picking the specific persons they want - the
consultants are assumed to be interchangeable.</p>
<p>On the upside for the consultants, if you work for a company with a good
reputation, you’re assumed to be competent. Bullshit interviews and
whiteboarding are much more rare.</p>
<p><strong>Contractors are second-class citizens.</strong> You may be excluded from internal
communications, meaning that nobody will tell you about the client’s product
strategy, or the upcoming office renovation - even though you may work at the
office every day<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> for years. They’re even less likely to ask your
opinion.</p>
<p>If you’re driven to build great products, you can still do that as a contractor,
but it’s hard to have the same level of ownership as an employee could have. I
think this is partially because contractors exist outside of the organization
chart, so they cannot “officially” own anything.</p>
<p><strong>People management is weird.</strong> In the traditional setup, if you are an
individual contributor (IC), you’d have a manager who is your boss and whose
responsibilities include supporting you and your growth.</p>
<p>In the consulting setup, you don’t have a people manager at the client. There is
probably a some kind of project manager, but its not their responsibility to
support you. You probably have some kind of boss at the consulting company, but
typically they have very little insight into the day-to-day work at the client
and they cannot support you.</p>
<p>You may end up in a team where no two people have the same employer, which makes
peer-to-peer support harder to establish.</p>
<p><strong>Incentives are weird.</strong> Let me crudely over-simplify: the contractors do not
have incentives to ensure the business success of what they build, but they may
have incentives to generate more work for themselves and their company. For
employees, the incentives are opposite.</p>
<hr>
<p>My point is: from the day-to-day perspective, building your software development
organization on contractors is not an obvious recipe for success. I’d like to
understand why it is so big thing in the Finnish IT scene.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The interface was a single-page web application built with AngularJS running in Chrome. I took over its maintenance and let’s just say that when I later learned React and Reagent, I never looked back. By the way, I’ve heard that SpaceX built their flight interface with JavaScript running in Chromium. I don’t know if they use Angular. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>At least when there is no ongoing global pandemic. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Who is going to use the programming language?
https://quanttype.net/p/team-matters/
Sun, 31 May 2020 00:00:00 +0000https://quanttype.net/p/team-matters/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ants2_hu_ba51e78848341133.webp" type="image/webp" />
<img src="https://quanttype.net/images/ants2.jpg" width="600" height="400" alt="Ants crawling over a fallen tree lying on the ground. The tree is entirely white and looks like it has been lying there for a long time." loading="lazy" />
</picture>
</figure>
<p>When talking about programming languages, we often talk about the cool features
that we could use and the thriving ecosystems that we could leverage. But when
we’re choosing programming language for a project, what really matters is the
team that is going to use it.</p>
<p>Learning a new programming language – if you want to use it in anger – takes
time, even if you have a lot of experience. In addition to the language itself,
you have learn about architecture, ecosystem, deployment, and operations with
the new language.</p>
<p>For example, I was working in a team of experienced Clojure and Java developers
when the management decided invest in Python. We went along and started a bunch
of new projects in Python.</p>
<p>None of us had significant recent Python experience, but it has a reputation of
being an easy language. It took us a good while to figure out how to handle
dependencies, how to get a suitable test setup, how to structure our programs,
how to deploy them with our existing infrastructure. We didn’t actually get the
software into production use before I moved on, but I bet that would have taught
us a number of new lessons.</p>
<p>This was an investment with real opportunity cost: we had a tight six-month
schedule to ship a new product and we took weeks to learn Python. We could have
spent all that time on polishing product, had we used a language that we already
knew. Hopefully the investment pays off in the years to come.</p>
<p>In the short term, an experienced team using a programming language that they
know will always beat an experienced team using a programming language new to
them. In the long term things may be different.</p>
Automating spec-tools releases
https://quanttype.net/p/automating-spec-tools-releases/
Sun, 24 May 2020 00:00:00 +0000https://quanttype.net/p/automating-spec-tools-releases/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ants_hu_7d90bf514cbc21c8.webp" type="image/webp" />
<img src="https://quanttype.net/images/ants.jpg" width="600" height="400" alt="Ants crawling on an piece old, dry, partially decomposed wood." loading="lazy" />
</picture>
</figure>
<p>My employer, <a href="https://www.metosin.fi/en/">Metosin</a>, is well-known for <a href="https://github.com/metosin/">its Clojure open-source
libraries</a>. When people hear that I work for Metosin, they often ask if I
contribute to the development of the libraries. I do, but not so much in the
form of coding new features or bug fixes. My focus has been on maintainership
tasks such as creating releases.</p>
<p>At the moment, we do not have a well-defined process or schedule for releases.
Personally, I believe in small releases. If a library has some unreleased work
and it looks like there won’t be more changes in the immediate future, I’ll do a
release.</p>
<p>The actual release process goes something like this:</p>
<p><strong>Merge ready PRs.</strong> First I check out if there are any open PRs that could be merged. If yes, I merge them.</p>
<p><strong>Update the changelog.</strong> I go through the PRs merged since the last release and add them to <code>CHANGELOG.md</code>. If there are breaking changes, I mark them as such.</p>
<p><strong>Update the version number.</strong> If there are breaking changes, I bump the minor or the major number. If not, bumping the patch number is enough.</p>
<p><strong>Build and deploy the release.</strong> On a good day, this means running <code>lein release</code>.</p>
<p><strong>Announce the release.</strong> Usually just on <a href="http://clojurians.net/">Clojurians Slack</a>.</p>
<p>This takes quite a bit of manual work and I’d like lessen the load for myself
and the other maintainers. As an experiment, I decided to automate
build-and-deploy step for <a href="https://github.com/metosin/spec-tools">spec-tools</a>. It’s a small step, but it’s
the easiest to automate. It’s also something that we get regularly wrong:</p>
<ul>
<li>Many of our releases are missing git tags and some of those tags are wrong.</li>
<li>Some of the releases have been built against dirty work trees - that is, they contain uncommited code.</li>
<li>If there’s Java code (like in jsonista and reitit), it won’t work with Java 8
unless it’s compiled with Java 8. The release is basically broken if you
build it while running Java 11.</li>
<li>Signing the artifacts with GPG constantly goes wrong.</li>
</ul>
<p>I used GitHub’s Release feature, GitHub Actions, and a bit of shell scripting to automate the build.</p>
<ul>
<li>There’s GitHub Actions <a href="https://github.com/metosin/spec-tools/blob/master/.github/workflows/release.yml">a workflow</a> that runs when you create a new
GitHub release. Each GitHub release corresponds to a git tag. The workflow
checks out the code for the tag and runs <code>lein deploy</code>.</li>
<li>There’s <a href="https://github.com/metosin/spec-tools/blob/master/scripts/release.sh">a shell script</a> that does a couple of sanity checks and
creates a new GitHub release from the head of <code>master</code> branch. Me and the other
maintainers can run it whenever we want to publish a new release.</li>
<li>The release artifacts are <em>not</em> signed with GPG. If somebody managed to steal
the Clojars deploy token used by GitHub actions, they would be able to steal
the GPG key as well. And anyway, I don’t think anyone checks the signatures.</li>
</ul>
<p>It seems to work! I hope to start to use this in more projects – especially the
Java 8 problem would be nice to automate away.</p>
<hr>
<p>There’s room for improvement, for sure. For example, the GitHub Release text is
now just whatever is the latest commit message, and we could trigger a cljdoc
build right after the release is deployed.</p>
<p>To go further, we could use a tool like <a href="https://github.com/release-drafter/release-drafter">release-drafter</a> to generate the
changelog from pull request titles. We’ve used release-drafter for work projects
with good results and <a href="https://quanttype.net/posts/2018-08-11-fully-automated-releases.html">I’ve experimented with something similar</a> before.
This would save a lot of work.</p>
Have you seen the swan?
https://quanttype.net/p/toolonlahti/
Sun, 17 May 2020 00:00:00 +0000https://quanttype.net/p/toolonlahti/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/tiira_hu_9c08e85481ec31e6.webp" type="image/webp" />
<img src="https://quanttype.net/images/tiira.jpg" width="600" height="480" alt="A view of Töölönlahti. The opera house is on the other side of the bay. There's a tern flying." loading="lazy" />
<figcaption>Töölönlahti is surprisingly good bird spot in central Helsinki</figcaption>
</picture>
</figure>
<p>How have you spent the copious free time given to you by the COVID-19 isolation?
I have been watching the urban nature.</p>
<p>Usually in May, I’m spending the weekends at the summer cottage and in
<a href="https://quanttype.net/posts/2018-05-15-a-night-in-nuuksio.html">Nuuksio</a> and in the nature in general. This year travelling and even
using public transport has been discouraged, so I’ve taken walks in the nearby
parks.</p>
<p>It’s funny, but I’ve probably paid more attention to the flora and the
fauna than ever before. When you visit the same places again and again, you have
more time to look at them and you start noticing the changes. It’s spring, so
flowers pop up, trees get leafs, and new birds start showing up.</p>
<p>One of my favorite sights right now is the <a href="https://www.inaturalist.org/observations/43576671">brooding mute swan</a> in
Töölönlahti. It’s been there for a couple of weeks - hopefully soon we get to
see some cygnets.</p>
<p>You also start to notice the gaps in your knowledge. I have no idea of the names
of the flowers and there are so many birdsongs that I <em>should</em> recognize but I
do not. I’ve been slowly looking them up. Eventually I’ll learn.</p>
Essential features of data specification libraries
https://quanttype.net/p/essential-features/
Sun, 10 May 2020 00:00:00 +0000https://quanttype.net/p/essential-features/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pipes2_hu_ec0110035f9ad081.webp" type="image/webp" />
<img src="https://quanttype.net/images/pipes2.jpg" width="600" height="400" alt="A look towards the sky through the pipes of Sibelius Monument" loading="lazy" />
</picture>
</figure>
<p>Last week I took a look at three data specification libariers in Clojure:
<a href="https://quanttype.net/posts/2020-05-03-schema-spec-and-malli.html">Schema, Spec, and Malli</a>. This
week, let’s talk about the essential features of these libraries.</p>
<p><strong>Data specification language.</strong> This is the foundation of every data
specification library: a way to describe the data with a schema. You could use
an existing language such as <a href="https://json-schema.org/">JSON Schema</a>, but in
practice that’s clunky and everybody develops their own language.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; Let's model users. We want to know the user's name and the year of birth.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Schema</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">schema.core</span> <span class="ss">:as</span> <span class="nv">schema</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">User</span> <span class="p">{</span><span class="ss">:name</span> <span class="nv">schema/Str</span>, <span class="ss">:year-of-birth</span> <span class="nv">schema/Int</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Spec</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.spec.alpha</span> <span class="ss">:as</span> <span class="nv">spec</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/def</span> <span class="ss">::year-of-birth</span> <span class="nv">int?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/def</span> <span class="ss">::name</span> <span class="nv">string?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/def</span> <span class="ss">::user</span> <span class="p">(</span><span class="nf">spec/keys</span> <span class="ss">:req-un</span> <span class="p">[</span><span class="ss">::name</span> <span class="ss">::year-of-birth</span><span class="p">]))</span>
</span></span></code></pre></div><p><strong>Validation.</strong> The basic operation is to validate data against a schema. What
is interesting is what happens when the validation fails. Easy-to-read error
messages are essential for the developers, but having errors as data is a useful
building block, too. For example, you could build front-end form validation on top of
a data specification library, and then you’d know what was the problem and in which field.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; Our user data example is missing the year of birth. Who gives out their</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; real year of birth, anyway, to the services we (the software industry) build?</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">a-user</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"[email protected]"</span>, <span class="ss">:year-of-birth</span> <span class="nv">nil</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">schema/validate</span> <span class="nv">User</span> <span class="nv">a-user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Execution error (ExceptionInfo) at schema.core/validator$fn (core.clj:155).</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Value does not match schema: {:year-of-birth (not (integer? nil))}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/valid?</span> <span class="ss">::user</span> <span class="nv">a-user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/explain</span> <span class="ss">::user</span> <span class="nv">a-user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; nil - failed: int? in: [:year-of-birth] at: [:year-of-birth] spec: :user/year-of-birth</span>
</span></span></code></pre></div><p><strong>Conforming.</strong> This feature is only implemented by Spec, but the idea is more
general. A data specification language allows you to declare a grammar for your
data structure. Conforming is parsing your data against that grammar. The result
is akin to an abstract syntax tree, and Spec calls the conversion in the other
direction “unforming”.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="c1">;; The regular expression specs and spec/or reveal the power of conforming.</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Let's model hiccup-style HTML data.</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/def</span> <span class="ss">::hiccup</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">spec/cat</span> <span class="ss">:tag</span> <span class="nv">keyword?</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:options</span> <span class="p">(</span><span class="nf">spec/?</span> <span class="nv">map?</span><span class="p">)</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:children</span> <span class="p">(</span><span class="nf">spec/*</span> <span class="p">(</span><span class="nf">spec/or</span> <span class="ss">:tag</span> <span class="ss">::hiccup</span>, <span class="ss">:text</span> <span class="nv">string?</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Now let's parse an anchor tag into a neat map.</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/conform</span>
</span></span><span class="line"><span class="cl"> <span class="ss">::hiccup</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:a</span> <span class="p">{</span><span class="ss">:href</span> <span class="s">"http://www.example.com/"</span><span class="p">}</span> <span class="s">"Check out "</span> <span class="p">[</span><span class="ss">:i</span> <span class="s">"example.com"</span><span class="p">]])</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; {:tag :a,</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; :options {:href "http://www.example.com/"},</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; :children [[:text "Check out "]</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; [:tag {:tag :i,</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; :children [[:text "example.com"]]}]]}</span>
</span></span></code></pre></div><p><strong>Coercion.</strong> Coercion, or more generally, schema-driven transformation of data
means that you walk your data and schema together and apply transformations to
build new data. This allows you to do things like <a href="https://quanttype.net/posts/2019-11-20-coercing-json-with-malli.html">converting date strings in
JSON to java.time instants</a> and vice versa.</p>
<p><strong>Program instrumentation.</strong> Yet another use case for data validation is to
define a contract for a function: what kind of inputs it takes, what kind of
data it returns, and how these are related. This is what Spec’s <code>fdef</code> does.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.spec.test.alpha</span> <span class="ss">:as</span> <span class="nv">stest</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">plus</span> <span class="p">[</span><span class="nv">x</span> <span class="nv">y</span><span class="p">]</span> <span class="p">(</span><span class="nb">+ </span><span class="nv">x</span> <span class="nv">y</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">spec/fdef</span> <span class="nv">plus</span> <span class="ss">:args</span> <span class="p">(</span><span class="nf">spec/cat</span> <span class="ss">:x</span> <span class="nv">int?</span> <span class="ss">:y</span> <span class="nv">int?</span><span class="p">)</span> <span class="ss">:ret</span> <span class="nv">int?</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">stest/instrument</span> <span class="o">`</span><span class="nv">plus</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">plus</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; => 3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">plus</span> <span class="mi">1</span> <span class="s">"2"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; Execution error - invalid arguments to user/plus at (REPL:1).</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; "2" - failed: int? at: [:y]</span>
</span></span></code></pre></div><p><strong>Schema introspection.</strong> The ability to inspect the schema enables features
such as generating JSON Schema from your schemas, as <a href="https://cljdoc.org/d/metosin/spec-tools/CURRENT/doc/json-schemas">shown by spec-tools</a>.</p>
<p><strong>Performance.</strong> Data specification libraries can end up playing a pretty
important role in your application. For REST APIs, JSON coercion is part of
every request, and if you use instrumentation, validation is literally
everywhere.</p>
<p>In my experience, indiscriminate use of Spec’s instrumentation
makes programs crawl. Michael Borkent shared a similar experience <a href="https://overcast.fm/+OZfIq7ERg/42:15">on The REPL
podcast</a>:</p>
<blockquote>
<p>If you have an application and you have like 100 core specs and then you start
instrumenting those, the application becomes really really slow, even in just
in development. So the overhead of calling spec validations on every function
call in Clojure program becomes too much for core functions. That is what I
had found and I was a little bit disappointed that it didn’t work out, so, at
least for dev purpose.</p>
</blockquote>
<p>The performance matters and your data specification library can become a bottleneck.</p>
<hr>
<p>As far as I know, no data specification library for Clojure has all of the
features mentioned above. Hopefully this list helps you to choose one – or to
decide to roll your own!</p>
Schema, Spec, and Malli
https://quanttype.net/p/schema-spec-and-malli/
Sun, 03 May 2020 00:00:00 +0000https://quanttype.net/p/schema-spec-and-malli/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pipes_hu_744e2d27352ddf5.webp" type="image/webp" />
<img src="https://quanttype.net/images/pipes.jpg" width="600" height="400" alt="The pipes of Sibelius Monument from below. There's a small patch of sky visible between the pipes." loading="lazy" />
</picture>
</figure>
<p>There are a number of data specification libraries for Clojure. The best-known
ones are <a href="https://github.com/plumatic/schema">Schema</a> and <a href="https://clojure.org/about/spec">clojure.spec</a>. Then there’s <a href="https://github.com/metosin/malli">Malli</a>,
designed by my colleagues at Metosin.</p>
<p>The libraries are used in two ways. They’re used for specifying the shape of
data inside the program, for example the parameter and return types of a
function. They’re also used for specifying the external interfaces of the
program, for example what kind of JSON a REST API endpoint accepts. I’ll call
these use cases <em>internal</em> and <em>external</em>.</p>
<ul>
<li>Schema was designed <a href="https://plumatic.github.io/schema-for-clojurescript-data-shape-declaration-and-validation">for internal use</a>, but it has seen a lot of
external use via libraries such as <a href="https://github.com/metosin/compojure-api">compojure-api</a>.</li>
<li>Spec was designed for internal use. For example, its support for conforming
(normalization) and regular expressions over data structures are great for
parsing little languages in macros and configuration.</li>
<li>Malli was designed for external use. For example, the robust support for
schema-driven transformations is a key feature (see e.g. my basic <a href="https://quanttype.net/posts/2019-11-20-coercing-json-with-malli.html">JSON
coercion example</a>) and the schema language of Malli is designed to
be serializable.</li>
</ul>
<p>Both Malli and Spec can be extended to the other use case via libraries such as
<a href="https://github.com/metosin/spec-tools">spec-tools</a> and <a href="https://github.com/teknql/aave">aave</a>.</p>
<p>So which one should you use? I don’t think there’s one obvious answer at the
moment. Here’s how the situation looks to me:</p>
<ul>
<li>Schema is a proven choice, but it won’t be getting any new features. If it
does what you need, great.</li>
<li>Spec is the best option for the internal use case and it has a nice ecosystem
of libraries such as <a href="https://github.com/bhb/expound">Expound</a> built around it. However, spec isn’t exactly easy to extend
and I’m saying this as one of the authors of spec-tools.
Spec’s alpha2 fixes some problems and brings new features such as
<a href="https://github.com/clojure/spec-alpha2/wiki/Schema-and-select">select</a>, but the development has been slow and I don’t know if
there’s a good story about how we’re going to migrate the ecosystem to alpha2.</li>
<li>Malli has great features for the external use case and it’s easy to build on.
There’s just one downside… it has not been released yet. Go for it if you’re
okay with using a library that will get some potentially-breaking
<a href="https://github.com/metosin/malli/milestone/1">changes</a> before its first release.</li>
</ul>
<p>Thus, it depends, but more so than usual. You have to consider your use case and
your capacity for bearing technical risks.</p>
Elegant knowledge transfer
https://quanttype.net/p/elegant-handoffs/
Sun, 26 Apr 2020 00:00:00 +0000https://quanttype.net/p/elegant-handoffs/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rantaa_hu_1b792e81eac7d014.webp" type="image/webp" />
<img src="https://quanttype.net/images/rantaa.jpg" width="600" height="450" alt="A landscape with reeds on the left and forest on the right. A kite is flying in the sky." loading="lazy" />
</picture>
</figure>
<p>Let’s say you’re leaving a software team and somebody else is joining the team
to replace you. How do you efficiently transfer knowledge to the new person in
short time?</p>
<p>My answer is that you… don’t.</p>
<p>You can bring on the new member in the usual way. Maybe somebody gives a presentation, maybe you’ll pair-code. The new
person starts with some easy tasks that act as a good introduction.</p>
<p>I hope you have a great README. The common knowledge should ideally be encoded
in the shared artifacts of the team: the software itself, the documentation, and
the project management tools. This helps with onboarding.</p>
<p>You can’t, however, teach everything an individual has learned over the course
of months and years in a week or two - unless you’ve poured a lot of effort into
making it teachable.</p>
<p>If you have specialized knowledge, you can try to teach it to the existing team
members instead of the newcomer. At least you have a lot more of common ground
with them.</p>
<hr>
<p>I believe people understand this intuitively yet I keep seeing well-resourced
organizations doing rushed hand-offs. I assume it’s to keep costs down. It
causes a loss of tacit knowledge, but that’s hard to measure so it’s assumed to
be essentially free.</p>
Ricoh GR III, a year later
https://quanttype.net/p/ricoh-gr-iii-one-year-later/
Sun, 19 Apr 2020 00:00:00 +0000https://quanttype.net/p/ricoh-gr-iii-one-year-later/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ricohgr3_hu_69d01a5501b15ead.webp" type="image/webp" />
<img src="https://quanttype.net/images/ricohgr3.jpg" width="600" height="450" alt="Ricoh GR III, a pocket camera, lying on a plywood surface, lens facing to the viewer. The camera is a bit dusty and the ring cap is missing, exposing a metal connector around the lens." loading="lazy" />
<figcaption>The red buttons are the anchors for Peak Design’s camera straps.</figcaption>
</picture>
</figure>
<p>I got my Ricoh GR III in April 2019. Back then, I thought that <a href="https://quanttype.net/posts/2019-05-26-initial-impressions-ricoh-gr-iii.html">it’s a pretty
great pocketable camera</a>. Now it’s time to take a second look.</p>
<p><em>Background</em>: Ricoh GR III is a fixed lens compact camera with a fast, 28
mm-equivalent lens. It’s a successor to GR II, which has cult following in the
street photography circles.</p>
<p>I still haven’t explored the camera that much and, honestly, that’s a good sign.
I quickly settled with a workflow and the camera has faded into the background.
I’ve been focusing on the photos, not on operating the camera. <em>This is how
it should be.</em></p>
<p><strong>The good.</strong> The camera fits in my pocket and it’s quick to operate with one
hand. Focusing with the touchscreen is great. I like the colors in
straight-out-of-camera JPEGs.</p>
<p>Initially I used the camera with a neck strap, but nowadays I just keep it in my
hand or stash it in the pocket. When I put it in the bag, I use a small Pelican
case (1020 Micro Case) that just fits the camera and the charging cable. It’s
sturdy but a bit heavy.</p>
<p>The firmware updates solved my problems with autofocus and the battery life has
been good enough. When <a href="https://quanttype.net/posts/2019-10-24-karhunkierros.html">I hiked Karhunkierros</a>, I recharged the
camera only once despite the cold weather, and I took a lot of photos.</p>
<p><strong>Flaws.</strong> The P mode still skews towards wide apertures. You can easily shift
it with the front dial, but I sometimes forget to do it and get photos with too
shallow depth of field.</p>
<p>I’ve discovered a new flaw in the camera. The camera comes a ring cap that
protects the lens barrel. The cap can be detached to attach a wide conversion
lens. The flaw is that cap sometimes comes off by itself. I dropped the cap
somewhere and a new one costs 40 €, so now I don’t have a cap.</p>
<p>I sometimes wish I had zoom, and on sunny days, a viewfinder would be nice.
However, the lack of these features is part of what makes the camera so great
for me. Can’t get everything.</p>
<p><strong>In conclusion.</strong> Still great.</p>
Put files where they are expected
https://quanttype.net/p/put-files-where-they-are-expected/
Sun, 12 Apr 2020 00:00:00 +0000https://quanttype.net/p/put-files-where-they-are-expected/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rantaruoko_hu_cb072b34d86017ba.webp" type="image/webp" />
<img src="https://quanttype.net/images/rantaruoko.jpg" width="600" height="400" alt="Reeds on a beach. A kite is flying in the sky behind them." loading="lazy" />
</picture>
</figure>
<p>When creating a new file for your programming project, how do you choose how to
name it and where to put it?</p>
<p>I recommend choosing a location that is expected by both humans and programs.
Here are a couple of examples:</p>
<p><strong>README</strong>. There’s a decades-long convention of putting the basic overview documentation
for a project in a file called <code>README</code>. This convention is now recognized by
tools: for example, GitHub renders it in the repository front page.</p>
<p><strong>Docker Compose</strong>. If you use docker-compose to set up the development
environment, you could put its configuration file with a custom name in some
subdirectory and use <code>docker-compose -f path/to/my/config-file</code>… or you
could put it in <code>docker-compose.yml</code> in repository root and ignore the <code>-f</code>
parameter. Then <code>docker-compose up</code> would be enough to start the dev
environment.</p>
<p><strong>Clojure tests</strong>. When you create tests for your Clojure project, you could
name them arbitrarily… or you could put tests for the namespace <code>a.b.c</code> in
<code>a.b.c-test</code> and you can enjoy using Projectile’s and Cursive’s “jump between
test and implementation” feature. Cursive even has a nice feature for creating
new tests, but it assumes this naming convention.</p>
<p>If you do what the humans expect, they don’t have to ask questions or look up
the documentation. If you do what the programs expect, they’re more ergonomic to
use.</p>
Video calls are cool but have you tried writing
https://quanttype.net/p/writing-and-remote/
Sun, 05 Apr 2020 00:00:00 +0000https://quanttype.net/p/writing-and-remote/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pasila_hu_b01b4bb92126358f.webp" type="image/webp" />
<img src="https://quanttype.net/images/pasila.jpg" width="600" height="480" alt="A train in a railyard. The Mall of Tripla shopping center can be seen in the background." loading="lazy" />
</picture>
</figure>
<p>It takes some skill to work remotely. When migrating from office to remote work,
you can’t just set up some communications tools and expect the work to <em>work</em> as
if nothing happened. You and everybody else have to learn how to use the tools
to collaborate.</p>
<p>Now software development organizations are mass-migrating to remote work.
Everybody is setting up Zoom or Teams that to enable video calls and learning to
raise their hand to get their turn to speak. That’s a great start, but the real
<em>remote transformation</em> (in Finnish: <em>etäloikka</em>?) will happen when you move to
asynchronous communication. That means communicating by writing.</p>
<p>Replacing meetings with asynchronous communication in text has a couple of
benefits.</p>
<p><strong>Your schedule is not dictated by meetings.</strong> You can manage your time and your
energy yourself. You don’t have to sit through irrelevant meetings in full and
this gives you time to focus the issues that really need your attention.</p>
<p><strong>There are fewer interrputions.</strong> They can’t interrupt you if they can’t reach
you.</p>
<p><strong>Text has great features.</strong> Text can be reread, searched, and shared easily.</p>
<p>Organizations usually already have good-enough tools for this. For example, we
have a chat, a wiki, an issue tracker, a code review tool, and there’s always
e-mail. It’s more about the mindset and developing the skills. Do you really
need a meeting?</p>
<p>Video calls are a great tool when you need it. Personally I think that they’re
great for creating a feeling of human connection in a way that is hard to have
in text. I just wanted to say that you can go further.</p>
Freezing deployments is risky
https://quanttype.net/p/feature-freeze/
Sun, 29 Mar 2020 00:00:00 +0000https://quanttype.net/p/feature-freeze/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/logbog_hu_4946333ff4723019.webp" type="image/webp" />
<img src="https://quanttype.net/images/logbog.jpg" width="600" height="480" alt="A piece of wooden board in a bog pond" loading="lazy" />
<figcaption>A log in a bog.</figcaption>
</picture>
</figure>
<p>If you’re running mission-critical software, how do you minimize the risk of it
breaking during these difficult times? Releases cause problems, so it can be
tempting to require an approval of each release by a change advisory board. You
could even declare a <em>deployment freeze</em>: no new versions of the software can be
deployed unless absolutely necessary.</p>
<p>Releases are inherently risky and a freeze does reduce this risk by making the
releases rarer. On the other hand, it makes the relases even more risky than
usual by making them bigger. Because releases become rare and it’s hard to get
an approval, people will cram as many fixes and features as they can into each
release</p>
<p>If you start with a reasonably reliable release process, I believe that making
releases bigger and rarer causes more problems than it solves. <a href="https://services.google.com/fh/files/misc/state-of-devops-2019.pdf"><em>The Accelerate
State of Devops Report 2019</em></a> concurs. They write about heavyweight
change processes (pp. 50-51):</p>
<blockquote>
<p>We found that formal change management processes that require the approval of
an external body such as a change advisory board (CAB) or a senior manager for
significant changes have a negative impact on software delivery performance.</p>
</blockquote>
<p>According to their research, the heavyweight processes increase change fail
rates. They conclude that “Analysis suggests this approach will make things
worse.”</p>
Working from home: initial impressions
https://quanttype.net/p/working-from-home/
Thu, 12 Mar 2020 00:00:00 +0000https://quanttype.net/p/working-from-home/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/laituri_hu_9ef1d5be759e6179.webp" type="image/webp" />
<img src="https://quanttype.net/images/laituri.jpg" width="600" height="489.5" alt="The remains of a pier in sea. There's a bridge in the background." loading="lazy" />
</picture>
</figure>
<p>COVID-19 is out there and like many, I’m working from home. Allow me to offer my
unique perspective.</p>
<p>Not everyone can work from home - not all jobs can be done remotely and even if
they can, <a href="https://puzzling.org/politics-and-society/labour/2020/03/employees-needing-the-most-support-during-an-abrupt-telecommuting-transition/">there are many obstacles</a> to working from home. For
example, the people you share your home with - partners, kids, roommates - have
to give you enough physical and mental space so that you can work. And they have
to have space for their work, too!</p>
<p>Myself, I’m lucky and privileged enough to pull it off. My biggest gripe is that
I don’t have a proper home office, so I’m working at our dinner table. It’s not
ergonomic enough for the long term, but I can handle it for a couple of weeks.</p>
<p>The organization I’m working with has previously given the option of working 1-2
days per week remotely, but right now everybody is recommended to work remotely
full time. This is the first time we’re remoting <em>in anger</em>.</p>
<p>I’ve worked remotely occassionally for ages, but software development is team
work. Being productive is not just about the individual. The partial remoting
was not enough for the organization to develop proper remote work capability, so
this has been a an interesting experience.</p>
<p><strong>Connections:</strong> Most of the developers do not have VPN connection to the
internal network, due to the usual kind of enterprise bureacracy. We just made
some changes that increase the need to access the internal network. What a great
timing!</p>
<p>The issue is getting resolved now with high priority, but it’s a bit sad if it
takes a pandemic to get a working VPN.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><strong>Meetings:</strong> It was always possible to attend meetings remotely via a video
call, but it wasn’t great compared to showing up: it’d be hard to follow what is
being said in person, the screensharing didn’t always work, etc.</p>
<p>Now when the meetings are conducted online-first, this stuff <em>has</em> to work.
Turns out it does. We’ve made a lot of progress on this front and
I’ve already had fun, productive meetings this way!</p>
<p><strong>Communications:</strong> It’s common advice that you should <em>overcommunicate</em>
when working remotely and it’s true. You have to <a href="https://tomcritchlow.com/2019/11/18/yes-and/">perform the work</a>
even more than in the office. You aren’t bumping into people at the coffee
machine, after all. The writing skills that I’ve honed by IRCing for two decades
now get to shine on Slack.</p>
<p>At the office, if you ever wonder what is going on, at least you can look around
and go talk to the people. When you’re remote, all there is Slack. Quiet Slack
is of no use, but people are warming up.</p>
<p><strong>The coffee machine:</strong> So what’s the online version of bumping into people at
the coffee machine? I don’t know yet but I’m sure something will appear.</p>
<p>Once the epidemic has passed, the organization will return to office work. I
hope that the remote work skills we develop now carry over and allow fully
productive partial remoting in the future.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Based on what I’m hearing from my software developer friends, this is by no means a unique experience. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Programming is writing
https://quanttype.net/p/programming-is-writing/
Tue, 25 Feb 2020 00:00:00 +0000https://quanttype.net/p/programming-is-writing/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kivi_hu_22e9b38caad53002.webp" type="image/webp" />
<img src="https://quanttype.net/images/kivi.jpg" width="600" height="489.5" alt="A rock in the sea, surrounded by a crust of thin ice." loading="lazy" />
</picture>
</figure>
<p>When you write programs, you have two audiences: the humans who read the code
and the computers that analyze and execute the code. The both audiences have to
understand the code.</p>
<p>Humans read programs like a hypertext<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>: not from start to end but by
jumping around following references.</p>
<p>For humans, the careful choice of terminology is of utmost importance.
Consistency matters. The computers do not care. They care about the grammar,
though.</p>
<p>Elegant programs are easy for humans to understand and easy for computers to
execute.</p>
<p><em>Psst: Math is programming, ergo math is writing.</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A well-known example of a hypertext is <a href="https://en.wikipedia.org/">Wikipedia</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Making decisions without asking your boss
https://quanttype.net/p/advice-process/
Wed, 19 Feb 2020 00:00:00 +0000https://quanttype.net/p/advice-process/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kivei_hu_bf9cb8b89c897b9a.webp" type="image/webp" />
<img src="https://quanttype.net/images/kivei.jpg" width="600" height="489.5" alt="Rocks and thin ice in the shoreline." loading="lazy" />
</picture>
</figure>
<p>At work, we aim to have a self-managed organization instead of a hierarchical
one.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> One of the challenges is that how decisions should be made. In a
hierarchical organization you could ask your boss, but what if your boss does
not want to make every decision? You could seek consensus, but what if you can’t
reach a consensus and a decision nevertheless has to be made?</p>
<p>A practical solution is to use <a href="http://www.reinventingorganizationswiki.com/Decision_Making#In_practice">the advice process</a>. Anyone can make a
decision after seeking advice from:</p>
<ol>
<li>Everyone who will be meaningfully affected by the decision, and</li>
<li>Everyone who has expertise in the matter.</li>
</ol>
<p>As long as everyone is able to trust the process<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, this neatly solves the
problem of consensus.</p>
<p>For example, we were having problems with Flowdock (an enterprise chat service): the
mobile clients were constantly broken and the search wasn’t great. It quickly
became apparent that we wouldn’t reach a consensus about what we should do: some
people thought we should move to Slack, others (me included) preferred Zulip,
and some thought that continuing with Flowdock was the best option despite its
flaws.</p>
<p>A person announced that they’d be making a decision about the course of action
in two weeks. Since the chat is our most important communication tool, they
announced it to everyone in the company and solicited advice. People made
arguments about communication styles and social, technical and monetary
aspects of each solution. Once the deadline passed, the decision maker announced
the solution: we’d be moving to Slack as it had a bunch of practical advantages
and was also the most popular solution.</p>
<p>Even though my favorite solution didn’t get picked, I felt like this was a good
way to make the decision: everyone affected had a say and it was relatively
efficient, timeboxed process that was guaranteed to produce a decision.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You do not have to send me a link to <a href="https://www.jofreeman.com/joreen/tyranny.htm">Tyranny of
Structulessness</a>. It’s a great essay and I have read it and I still
believe that you can improve upon strictly hierarchical organizations.
Look, I don’t send a link to <a href="https://en.wikipedia.org/wiki/Moral_Mazes">Moral Mazes</a> or to <a href="https://www.ribbonfarm.com/2009/10/07/the-gervais-principle-or-the-office-according-to-the-office/">The Gervais
Principle</a> every time I hear somebody say that they work in a
hierarchical organization. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>You have to trust the decision maker. I’m not sure if trusting the
advice-givers is 100% necessary. It does help, of course. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
No need for something to say
https://quanttype.net/p/no-need-for-something-to-say/
Wed, 12 Feb 2020 00:00:00 +0000https://quanttype.net/p/no-need-for-something-to-say/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sydan_hu_48385732d6c50472.webp" type="image/webp" />
<img src="https://quanttype.net/images/sydan.jpg" width="600" height="489.5" alt="A bus over a bridge in Kalasatama." loading="lazy" />
</picture>
</figure>
<p>When you’re out there, struggling to create, staring at the blank page, the
blank canvas, or the blank MS Paint window, you might be thinking “I have
nothing to say”, and you might be right.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Yet that is not the problem, is it?</p>
<p>The problem is how to have great idea. <em>Having something say</em>, a message to the
world, can be a great source of creativity, but it’s not the only one. What else
is out there?</p>
<p>I challenge you to consider art. When you look at it, listen to it, experience
it, what do you see, hear, feel? Is there a message? Did the author want to say
something? Or was there something else driving them?<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Those without a message, I bet they were playing.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Maybe they looked at the
materials and went <em>hmm</em> or tried out a new technique and went <em>I wonder</em>. Maybe
they saw a constraint or a challenge and went <em>I can overcome that</em>. Maybe they
felt something and went <em>now that’s delightful</em>. Maybe they saw something and went
<em>but nobody else has ever seen this</em>. Maybe they just had good time playing or
painting or writing or dancing.</p>
<p>Play and messages: did I cover the sources of creativity exhaustively?</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You might be wrong. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>For many professional creatives, both historical and contemporary, earning a livelihood has been an important driver. Is that a source of ideas? <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>It could’ve been us, but. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Logging request IDs in Tornado
https://quanttype.net/p/request-id-logging/
Wed, 05 Feb 2020 00:00:00 +0000https://quanttype.net/p/request-id-logging/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/isoisansilta_hu_cc0a65d726ca8af1.webp" type="image/webp" />
<img src="https://quanttype.net/images/isoisansilta.jpg" width="600" height="489.5" alt="People walking over Isoisänsilta" loading="lazy" />
</picture>
</figure>
<p>When you’re debugging a web service, it’s handy if you can get all the log
entries associated with a single HTTP request. This is easy if you generate a
unique ID for each request and include it in all the log entries.</p>
<p>If your services calls other services, you can build a simple tracing system by
including this ID in all those calls. If you’re using nginx as a reverse proxy,
<a href="https://stackoverflow.com/a/38447872/297366">you can use it to generate the IDs</a>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>At work, we’re using the <a href="https://www.tornadoweb.org">Tornado</a> web framework in Python and we wanted to have
the request IDs in our logs. The most obvious solution is to pass the request ID
to every function that logs anything and manually include it in the logging
calls. This leads to cluttered code, though, and it’s easy to forget to add the
ID everywhere.</p>
<p>We wanted to have an easier, more <em>magical</em> solution and we found it in <a href="https://docs.python.org/3/library/contextvars.html">context variables</a> and log filters.</p>
<p>Context variables are “context-local” variables. They’re similar to thread-local
variables – and to Clojure’s dynamic vars – but they work correctly with
Python’s asyncio. Thus they’re a great choice for storing “request-local” data
such as the request ID.</p>
<p>In your handler, you can generate or extract the ID and store it in a context
variable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">contextvars</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tornado.web</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">request_id_var</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">ContextVar</span><span class="p">(</span><span class="s2">"request_id"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyHandler</span><span class="p">(</span><span class="n">tornado</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">RequestHandler</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># prepare is called at the beginning of request handling</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">prepare</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># If the request headers do not include a request ID, let's generate one.</span>
</span></span><span class="line"><span class="cl"> <span class="n">request_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"request-id"</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="n">request_id_var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">request_id</span><span class="p">)</span>
</span></span></code></pre></div><p>In my example, I’ve implemented <code>prepare</code> in the same class as my actual
handler, but in our real application, all our handlers inherit from a custom
base class implements request ID preparation and some other common features.</p>
<p>Python’s logging cookbook has <a href="https://docs.python.org/3/howto/logging-cookbook.html#adding-contextual-information-to-your-logging-output">two recipes</a> for adding contextual
information to logging output: LoggerAdapters and filters. We chose
<a href="https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information">filters</a> to avoid having to wrap loggers everywhere. Then we can use
the familiar pattern of getting a module-specific logger:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">logging</span>
</span></span><span class="line"><span class="cl"><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span></code></pre></div><p>The main use case for filters is limiting which log entries get emitted, but
they’re allowed to mutate the records. Our filter always returns <code>True</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyFilter</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">Filter</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">record</span><span class="o">.</span><span class="n">request_id</span> <span class="o">=</span> <span class="n">request_id_var</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span>
</span></span></code></pre></div><p>Unlike log handlers, filters do not propagate. This means that you have to add
the filter to every logger… or you can add it to the handler of your root
logger.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">my_filter</span> <span class="o">=</span> <span class="n">MyFilter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">handler</span> <span class="ow">in</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span><span class="o">.</span><span class="n">handlers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">handler</span><span class="o">.</span><span class="n">addFilter</span><span class="p">(</span><span class="n">my_filter</span><span class="p">)</span>
</span></span></code></pre></div><p>Here’s the full example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">contextvars</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">logging</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">uuid</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tornado.ioloop</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">tornado.web</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">request_id_var</span> <span class="o">=</span> <span class="n">contextvars</span><span class="o">.</span><span class="n">ContextVar</span><span class="p">(</span><span class="s2">"request_id"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Let's have an async function for the sake of demonstration</span>
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">generate_number</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"generate a number"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyHandler</span><span class="p">(</span><span class="n">tornado</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">RequestHandler</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># prepare is called at the beginning of request handling</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">prepare</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># If the request headers do not include a request ID, let's generate one.</span>
</span></span><span class="line"><span class="cl"> <span class="n">request_id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"request-id"</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid4</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="n">request_id_var</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">request_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">async</span> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">number</span> <span class="o">=</span> <span class="k">await</span> <span class="n">generate_number</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Here's a number: </span><span class="si">{</span><span class="n">number</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">make_app</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">tornado</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">Application</span><span class="p">([(</span><span class="sa">r</span><span class="s2">"/"</span><span class="p">,</span> <span class="n">MyHandler</span><span class="p">),])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyFilter</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">Filter</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="n">record</span><span class="o">.</span><span class="n">request_id</span> <span class="o">=</span> <span class="n">request_id_var</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"-"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nb">format</span><span class="o">=</span><span class="s2">"</span><span class="si">%(levelname)s</span><span class="s2"> </span><span class="si">%(request_id)s</span><span class="s2"> </span><span class="si">%(message)s</span><span class="s2">"</span><span class="p">,</span> <span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Log filters do not propagate, but handlers do. Thus we add the filter</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># to the handlers of the root logger so that the messages of child loggers</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># get filtered as well.</span>
</span></span><span class="line"><span class="cl"> <span class="n">my_filter</span> <span class="o">=</span> <span class="n">MyFilter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">handler</span> <span class="ow">in</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span><span class="o">.</span><span class="n">handlers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">handler</span><span class="o">.</span><span class="n">addFilter</span><span class="p">(</span><span class="n">my_filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">port</span> <span class="o">=</span> <span class="mi">8000</span>
</span></span><span class="line"><span class="cl"> <span class="n">app</span> <span class="o">=</span> <span class="n">make_app</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="n">app</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">port</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">"Listening at http://localhost:</span><span class="si">%d</span><span class="s2">/"</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">tornado</span><span class="o">.</span><span class="n">ioloop</span><span class="o">.</span><span class="n">IOLoop</span><span class="o">.</span><span class="n">current</span><span class="p">()</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p><em>The example is also available <a href="https://gist.github.com/miikka/6c4d7c5035ab591fa9362aba486e5c86">as a Gist</a></em>.</p>
<p>If you run it (remember to <code>pip install tornado</code>) and do a HTTP request, you will see something like this:</p>
<pre tabindex="0"><code>INFO - Listening at http://localhost:8000/
INFO f00e793f-73e5-4210-8709-41aefe839e5a generate a number
INFO f00e793f-73e5-4210-8709-41aefe839e5a 200 GET / (::1) 1.27ms
</code></pre><p><em>See also my other posts about <a href="https://quanttype.net/tags/python.html">Python</a>.</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>For a more sophisticated distributed tracing system, take a look at something like <a href="https://opentracing.io">OpenTracing</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Hello again, Python
https://quanttype.net/p/hello-again-python/
Sun, 19 Jan 2020 00:00:00 +0000https://quanttype.net/p/hello-again-python/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hakone3_hu_c1a7b01593efe086.webp" type="image/webp" />
<img src="https://quanttype.net/images/hakone3.jpg" width="600" height="400" alt="Shadows from clouds on the slopes of a Japanese mountain in spring." loading="lazy" />
</picture>
</figure>
<p>Thanks to some organizational surprises, I’m now developing web services in
Python. The last time I used Python <em>in anger</em> was in 2016. Has anything changed?</p>
<p><strong>Records</strong>. Python has records now, thanks to <a href="https://www.attrs.org/">attrs</a> and
<a href="https://docs.python.org/3/library/dataclasses.html">dataclasses</a>. Namedtuples were too simple and and writing classes
by hand for everything was too complicated. Attrs and dataclasses are just
right. I didn’t get this in 2016 when attrs was new, but I get it now. Attrs has
a nice overview of how it compares to <a href="https://www.attrs.org/en/stable/why.html">the other
solutions</a>.</p>
<p><strong>Type annotations.</strong> The type annotations are useful now. I’m using JetBrains
PyCharms as my IDE and it the displays type information doc popus and warns
about type errors. In the command-line, mypy works – and many third-party
libraries actually have type annotations! Python is no Haskell – heck, it’s
not even TypeScript – but this is a welcome development.</p>
<p><strong>Python 2 vs. Python 3</strong>. We’re finally in the era of Python 3. I hope.</p>
<p><strong>Package management</strong>. The tools have become more robust. I have so many
experiences of easy_install or pip or virtualenv mysteriously breaking. Now
everything seems to work. It’s still complicated but at least it works.</p>
<p>I’m pleasantly surprised. I still believe that <a href="https://quanttype.net/posts/2015-12-02-python-is-not-good-enough.html">Python leaves a lot to be
desired</a>, but undeniably the developer experience has improved!</p>
Joys of a heavy camera
https://quanttype.net/p/joys-of-a-heavy-camera/
Sun, 12 Jan 2020 00:00:00 +0000https://quanttype.net/p/joys-of-a-heavy-camera/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sorsat3_hu_8dc85f979c144f36.webp" type="image/webp" />
<img src="https://quanttype.net/images/sorsat3.jpg" width="600" height="480" alt="A medium-format camera on a tripod on a shore. There are two mallards swimming in the sea in the background." loading="lazy" />
</picture>
</figure>
<p>I’ve had three rolls of medium-format (120) film sitting in my fridge. They’ve
been there long enough that they have all expired already. It’s black-and-white
film, though, so it should be fine, especially since I’ve kept it refrigerated.
I’ve shot with decade-old films and the results were okay.</p>
<p>The <a href="https://quanttype.net/posts/2016-05-10-mamiya-rb67.html">Mamiya</a> is still
with me, so I decided to finally go and shoot the films. I headed to Mustikkamaa
for my “usual” set: ice, rocks, and reeds.</p>
<p>This winter has been unusually warm in Helsinki, so we do not have much ice. You
have to crop carefully to make the small bits of ice look like a winter
wonderland. At least there was enough ice under the Korkeasaari Zoo bridge to
make the weird packing noises.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/zoobridge_hu_7b882785aa40f082.webp" type="image/webp" />
<img src="https://quanttype.net/images/zoobridge.jpg" width="600" height="480" alt="A composition of rocks in the shore. There's a thin, broken layer of ice in the sea." loading="lazy" />
</picture>
</figure>
<p>It was my first session with the Mamiya in two years. My <a href="https://quanttype.net/posts/2019-05-26-initial-impressions-ricoh-gr-iii.html">usual camera</a> is
light and nimble. The Mamiya is the opposite: it’s heavy and cumbersome. It
challenges you in two ways.</p>
<p><strong>Every shot takes work.</strong> You have to adjust the tripod, measure the light,
cock the mirror, advance the film, remove the dark slide, and, finally, trigger
the shutter. It’s not cheap, either: the film and processing easily costs over 1
€ per frame. You’ll want to cover every motif with as few shots as possible.</p>
<p><strong>Finding a new location takes work.</strong> You have to lug around the camera and the
tripod while walking on icy rocks. You’ll want to get everything out of the spot
where you plop the tripod down.</p>
<p>This leads to slower, more deliberate photography experience. Every shot
matters. You’ll focus on the composition and on shooting on the critical moment.
For me, it’s a delight.</p>
<p>Sometimes you want to slow down. Sometimes you want to speed up. Heavy cameras
have their upsides.</p>
<p><em>The photos in this post are digital, but <a href="https://flic.kr/s/aHsmKK9vWN">click here to see the film shots on Flickr</a>.</em></p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mamiya_hu_4c47a505c4a37327.webp" type="image/webp" />
<img src="https://quanttype.net/images/mamiya.jpg" width="600" height="900" alt="The medium-format camera seen from the above. The viewfinder shows a landscape with rocky seashore and reeds in the front and a city in the background." loading="lazy" />
</picture>
</figure>
Yearnote 2019
https://quanttype.net/p/yearnote-2019/
Sun, 05 Jan 2020 00:00:00 +0000https://quanttype.net/p/yearnote-2019/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/puurijarvi_hu_bec8992a9d876144.webp" type="image/webp" />
<img src="https://quanttype.net/images/puurijarvi.jpg" width="600" height="400" alt="The barren scenery of frozen Lake Puurijärvi." loading="lazy" />
</picture>
</figure>
<p>New year, new shenanigans, as we say in Finland. A new decade! <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> I, on
the other hand, am back to my bullshit. Please allow me to talk about me in the
year 2019.</p>
<p>It was a difficult year for me, but I don’t want to delve on that too much.
Instead, let’s focus on the good stuff.</p>
<h2 id="working">Working</h2>
<p>I continued to work as a software developer. In autumn, I started to work for a
new client. This is my first time working for a big (in the Finnish scale)
tech organization. Finally I get to see in action all the big organization
dynamics that I’ve only read and heard about.</p>
<p>Working in a big organization is much more <a href="https://tomcritchlow.com/2019/11/18/yes-and/">performative</a> than in a
small one. It’s not enough to do work – the others must also know that you’ve
done it.</p>
<h2 id="thoughleadering">Thoughleadering</h2>
<p><a href="https://quanttype.net/posts/2019-01-01-yearnote-2018.html">Year ago</a> I wrote that I want to level up
my thoughtleadering game. I did succeed, in a small way!</p>
<ul>
<li>My post <a href="https://quanttype.net/posts/2019-03-06-handbrewing-coffee.html">on brewing coffee</a> made it
to Hacker News front page.</li>
<li>I gave a lightning talk at <a href="https://clojured.de">:clojureD</a>.</li>
</ul>
<p>These two experiences made me realize that I prefer blogging to public speaking,
both as a writer/speaker and as a reader/listener. Accordingly, I gave up on my
public speaking plans and started blogging regularly.</p>
<p>I attended three Clojure conferences in 2019: :clojureD, Heart of Clojure, and
ClojuTRE. Heart of Clojure was <a href="https://quanttype.net/posts/2019-08-12-hallway-track-conference.html">especially good</a>, but all of them were a
pleasure.</p>
<p>I don’t think I will attend as many conferences in 2020, but I hope to get a
chance to meet up again with at least some the cool people I’ve gotten to know
from the Clojure community.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/konjaku_hu_188dc8ba7453d65a.webp" type="image/webp" />
<img src="https://quanttype.net/images/konjaku.jpg" width="600" height="400" alt="A tray with two cups of amazake and a plate of plum-sized konjac balls." loading="lazy" />
<figcaption>The most distressing thing I ate in 2019: this combo of amazake and konjac balls.</figcaption>
</picture>
</figure>
<h2 id="traveling">Traveling</h2>
<p>In April, I travelled to Japan for three weeks. I loved to hike up the mountains
in Hakone and in Yakushima and to eat dorayakis at every occassion. This was my
first time in Japan, but I do get it now why so many people here in Finland love
the country. It’s such a <em>foreign</em> place, yet so easy to travel in.</p>
<p>In October, I <a href="https://quanttype.net/posts/2019-10-24-karhunkierros.html">hiked Karhunkierros</a>. It was my first solo hike and it left
me wanting more. I don’t have many plans for 2020, but doing another long hike
is one of them.</p>
<h2 id="traditional-commentary-on-finnish-politics">Traditional commentary on Finnish politics</h2>
<p>I can’t believe that Antti Rinne’s cabinet already fell apart. I hope that Sanna
Marin will have a better run.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>As a mathematician, I assert that the 20s begin in the year 2020. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Standard problems, standard solutions
https://quanttype.net/p/standard-solutions/
Thu, 19 Dec 2019 00:00:00 +0000https://quanttype.net/p/standard-solutions/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mutka3_hu_99412175c7d13c2f.webp" type="image/webp" />
<img src="https://quanttype.net/images/mutka3.jpg" width="600" height="400" alt="A curve of river in the middle of a forest. There's snow on the shore." loading="lazy" />
</picture>
</figure>
<p>I work as a software consultant. What we do is that we develop software for
other companies. Sometimes we do it as a team of our own and sometimes embedded
in an in-house development team. Sometimes the clients come to us for our
special expertise and sometimes they just need butts in the seats churning out
code.</p>
<p>Our job is to implement <strong>standard solutions for standard problems</strong>. The clients
are not in the high-tech business – or if they are, their in-house developers
work on the innovative secret sauce and we come in to build the scaffolding
needed for making the secret sauce in a complete product.</p>
<p>The main challenges are rarely technical. Instead, we need to understand the
business domain and figure out which of the usual problems need solving. Then
we need to figure out how to navigate the client organization to allow us to
build the usual solutions efficiently.</p>
<p>Our key deliverable is the software itself. Another deliverable is the process
that produced the software. If the software is to live on after we move on, the
process has to continue and evolve.</p>
<p>After all, software is done only once its last user stops using it.</p>
Just automate syntax formatting
https://quanttype.net/p/just-automate-formatting/
Thu, 12 Dec 2019 00:00:00 +0000https://quanttype.net/p/just-automate-formatting/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/polku_hu_80a55b66fb08ccab.webp" type="image/webp" />
<img src="https://quanttype.net/images/polku.jpg" width="600" height="400" alt="A path amid rocks and snow-covered trees. There's a rope handrail next to the path." loading="lazy" />
</picture>
</figure>
<p>Fighting over syntax formatting in code review is, mostly, waste of time. Having
consistent formatting is great for readability, but code review is not the right
place to enforce it. To <a href="https://quanttype.net/posts/2019-12-05-keeping-code-review-fast.html">keep code reviews fast and smooth</a>, you’ll want
to focus on high-impact issues. With formatting, the returns diminish quickly.</p>
<p>Instead, you should use computers to enforce consistent formatting. There are
two ways to go about it:</p>
<p><strong>Use a code formatter tool.</strong> Try to find a fast one and configure everybody’s
editor to run it on save, or use a git pre-commit hook. If you write Go, use
gofmt. If you write JavaScript, I hear <a href="https://prettier.io">Prettier</a> is a
popular one.</p>
<p><strong>Use matching editor configurations.</strong> If your programming language doesn’t
have a good code formatter, your next best bet is to configure everybody’s
editors to match each other as well as possible. This solution doesn’t really
scale, but for small teams it’s feasible. An <a href="https://editorconfig.org">EditorConfig file</a> can help a
bit.</p>
<p>For example, there’s no <a href="https://clojureverse.org/t/clj-commons-building-a-formatter-like-gofmt-for-clojure/3240">canonical formatter for Clojure</a>. Matching
the config is simple if everybody uses the same editor, but we’ve managed to get
close enough with Cursive and clojure-mode.</p>
<p>What do we do when the editor config mismatch causes unnecessary code
reformatting? We let it be.</p>
<p><strong>It’s better to accept some code churn than to fight over indentation in code review.</strong></p>
<p><em>See also my other posts about <a href="https://quanttype.net/tags/code-review.html">code review</a>.</em></p>
Keeping code review fast
https://quanttype.net/p/keeping-code-review-fast/
Thu, 05 Dec 2019 00:00:00 +0000https://quanttype.net/p/keeping-code-review-fast/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaa2_hu_c7580a94dcf1834d.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaa2.jpg" width="600" height="400" alt="Lumps of snow on a frozen lake." loading="lazy" />
</picture>
</figure>
<p>If you want mandatory code review to really suck, make it slow.</p>
<p>Power games aside, one of the most frustrating aspects of code review is how
long it takes and how many context switches it involves. There are two main things
that contribute to this:</p>
<ol>
<li>how long it takes for the reviewers to react to new and updated pull
requests (PRs).</li>
<li>how many rounds of review are needed</li>
</ol>
<p>In my experience in a 2-5 person team, if everybody is willing to dedicate some
time for reviews every day, the wait for reviews is never too long. If I can get
reviews for morning PRs in the afternoon and for the afternoon PRs in the next
morning, I’m pretty happy already.</p>
<p>If your team members are not willing to dedicate this time, usually it’s because
they either do not see code review as valuable – possibly because the management
does not see it as valuable! – or they don’t know how to do it. You’ll have to
solve those issues before you can improve the review velocity.</p>
<p>When reviewing, I use a three-level system of approvals:</p>
<ol>
<li>Not approved; changes are needed and the PR needs to be re-reviewed after the
changes.</li>
<li>Approved conditionally: the author needs to do some changes, but they can
merge the PR afterwards without another review round. For example, if I point
out something minor like a typo, I trust that the author can fix it themself.</li>
<li>Full approval. The PR can be merged as-is.</li>
</ol>
<p>Ideally a most of your PRs would be approved with a single review round with
some needing another round. There are a couple of things that help:</p>
<p><strong>Keep PRs small</strong>. A shorter PR is easier to review than a longer one.</p>
<p><strong>Give actionable feedback</strong>. The second round of review will go more smoothly if
the author knows exactly what changes the reviewer wants to see to approve the
PR.</p>
<p><strong>Automate what you can</strong>. If you care about consistent syntax formatting, use a
linter or an autoformatter to enforce the format. Then you can ignore it
entirely in the review process.</p>
<p><strong>Figure out what to do if consensus cannot be reached</strong>. Sometimes the author and
the reviewer can’t agree. My personal rule is that if there’s no obvious
authority, the author makes the decision. You want to be mindful of <a href="https://quanttype.net/posts/2019-11-28-power-of-code-review.html">power
dynamics</a>, though.</p>
<p><strong>Figure out what to do if no-one wants to approve the PR</strong>. For example,
reviewers may hesitate to approve the PR when they are not very familiar with
the codebase. My personal rule is that the PR should be approved unless the
reviews can give constructive feedback. The PR won’t get any better by sitting
on it.</p>
<p>If many of your PRs seem to need three or more rounds of review, you might want
to change some other part of the process than code review:</p>
<ul>
<li>Having a design discussion before implementing anything helps to ensure that
nobody has fundamental disagreement with the approach taken during the review
process.</li>
<li>Having clear coding standards helps to avoid unnecessary conflict in the
review phase.</li>
<li>Pair coding is more efficient than multiple rounds of review. If it looks like
a PR needs a lot of changes, consider pair coding the improvements with the
author. Then there are at least two people who think it’s an adequate
solution.</li>
</ul>
<p><em>See also my other posts about <a href="https://quanttype.net/tags/code-review.html">code review</a>.</em></p>
The power of code review
https://quanttype.net/p/power-of-code-review/
Thu, 28 Nov 2019 00:00:00 +0000https://quanttype.net/p/power-of-code-review/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/vaara_hu_da1d4370fe44be6.webp" type="image/webp" />
<img src="https://quanttype.net/images/vaara.jpg" width="600" height="400" alt="Snow-covered trees on the slopes of Konttainen and Valtavaara in Ruka." loading="lazy" />
</picture>
</figure>
<p>Last week, Camille Fournier tweeted <a href="https://x.com/skamille/status/1198060881168535552">this</a>:</p>
<blockquote>
<p>Questioning the value of mandatory code review is definitely the most popular underground belief held by senior engineers I know</p>
</blockquote>
<p>It launched a discussion about the merits of code review and a bunch of people
came forward about their bad experiences. For them, code review had become an
arena of power games and a place to demonstrate how smart you are.</p>
<p>I’ve experienced code review as a highly valuable practice and advocated for it,
but it’s easy to see how it could go wrong.</p>
<p>Sarah Mei has a talk called <a href="https://youtu.be/YL-6RCTywbc">The Power of Agile</a>.
She speaks about how the power differentials, such as the one between juniors
and seniors, cause problems in extreme programming practices such as pair
programming. Her key point is that the agile practices do not really address
these problems at all.</p>
<p>The same goes for code review as it is commonly practiced. It’s easy for
reviewers to bring the process to halt if they want to be gatekeepers or if they
are just a bit too pedantic for their own good. They can keep asking for more
changes, raise more concerns, and nitpick more.</p>
<p>This happens even when people see each other as equals. It must be worse when
you’re a member of a group whose expertise gets constantly questioned due to
prejudice. And when you’re a reviewer, there’s the problem of having your review
comments ignored.</p>
<p>I’m sure everyone who has extensive experience with code review has sometimes felt frustrated with the feedback they’ve gotten, but if it’s a constant source of frustration, something is wrong.</p>
<p>The junior-senior power differential in code review was always obvious to me – I was introduced to code review as a junior developer – but I’ve only just started to think about the more general power differentials. I hope to come back to this topic with more insight later, but I’m going to leave you with one thought and one recommendation.</p>
<p><strong>Thought:</strong> Developing software is collaborative team work. Tool-assisted code review is just one of the tools in the collaboration toolbox, along with pair programming. Reviewing is not an end in itself. The goal is not to produce “perfect” code. The goal is to deliver working software. When reviewing becomes power games, it hinders that goal.</p>
<p><strong>Recommendation:</strong> Alex Hill has a <a href="http://www.alexandra-hill.com/2018/06/25/the-art-of-giving-and-receiving-code-reviews/">great
post</a>
and a <a href="https://youtu.be/XY6eA2_2hOg">great talk</a> about how to give and receive
code reviews gracefully. You should follow her advice. It will make your reviews
more egalitarian and more fun.</p>
<p><em>See also my other posts about <a href="https://quanttype.net/tags/code-review.html">code review</a>.</em></p>
Coercing JSON with malli
https://quanttype.net/p/coercing-json-with-malli/
Wed, 20 Nov 2019 00:00:00 +0000https://quanttype.net/p/coercing-json-with-malli/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/suolampi_hu_d63e5f9e8a6eaf7a.webp" type="image/webp" />
<img src="https://quanttype.net/images/suolampi.jpg" width="600" height="400" alt="Snow-covered trees hang over a frozen lake" loading="lazy" />
</picture>
</figure>
<p>One of the problems with JSON is its limited selection of datatypes. For
example, if you want represent timestamps in JSON, you have to encode them as
strings or numbers. There’s no way to tag the specific values as timestamps, so
if you’re building an application that consumes such JSON, you have to build a
mechanism for coercing the strings or numbers into your programming language’s
timestamp datatype.</p>
<p>For example, consider JSON data like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">"start"</span><span class="p">:</span> <span class="s2">"2019-01-01T00:00:00Z"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"end"</span><span class="p">:</span> <span class="s2">"2019-01-31T23:59:59Z"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"description"</span><span class="p">:</span> <span class="s2">"The month of January"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"tags"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"month"</span><span class="p">]}</span>
</span></span></code></pre></div><p>We’re programming in Clojure, so what we would like to actually have is this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:start</span> <span class="o">#</span><span class="nv">inst</span> <span class="s">"2019-01-01T00:00:00.000-00:00"</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:end</span> <span class="o">#</span><span class="nv">inst</span> <span class="s">"2019-01-31T23:59:59.000-00:00"</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:description</span> <span class="s">"The month of January"</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:tags</span> <span class="o">#</span><span class="p">{</span><span class="ss">:month</span><span class="p">}}</span>
</span></span></code></pre></div><p>If you’re building a Clojure web backend and you’re receiving JSON via a HTTP
request, your <a href="https://github.com/metosin/reitit">routing library</a> probably has
a nice, schema-driven way of handling the coercion. When the data is coming from
some other sources, there’s usually no “built-in” solution.</p>
<p>I’ve seen a bunch of solutions to this problem. Sometimes the coercion code is
intermingled with business logic. This is not great: it’s hard to tell where and
when the data gets converted to the proper datatypes and when it’s just strings.
Usually the error handling is not great, either.</p>
<p>A better solution would be to have an explicit coercion function and call it
before handing the data to the business logic. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">coerce</span> <span class="p">[</span><span class="nv">data</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">-> </span><span class="nv">data</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">update</span> <span class="ss">:start</span> <span class="nv">parse-timestamp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">update</span> <span class="ss">:end</span> <span class="nv">parse-timestamp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">update</span> <span class="ss">:tags</span> <span class="o">#</span><span class="p">(</span><span class="nb">into </span><span class="o">#</span><span class="p">{}</span> <span class="p">(</span><span class="nb">map </span><span class="nv">keyword</span><span class="p">)</span> <span class="nv">%</span><span class="p">))))</span>
</span></span></code></pre></div><p>This is a good start, but it’d be nice to make this more declarative, just like
the API definitions in compojure-api and reitit. It’s not too hard to do this
yourself with <a href="https://github.com/plumatic/schema">Schema</a> or
clojure.spec, at least if you use <a href="https://github.com/metosin/spec-tools">spec-tools</a>. However, for the
sake of novelty, I’m going to use <a href="https://github.com/metosin/malli">malli</a>.</p>
<p>Malli is the new data specification library by Metosin. It’s more like
Schema than clojure.spec: its main use cases are data validation and
transformation at the edges of the program,whereas clojure.spec is focused more
on the shape of data inside the program. You can read more in <a href="https://github.com/metosin/malli#motivation">malli’s
README</a>. Malli has not yet been
released, but I think it’s starting to look promising.</p>
<p>Let’s define a schema, then:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">Event</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:map</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:start</span> <span class="nv">inst?</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:end</span> <span class="nv">inst?</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:description</span> <span class="nv">string?</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:tags</span> <span class="p">[</span><span class="ss">:set</span> <span class="nv">keyword?</span><span class="p">]]])</span>
</span></span></code></pre></div><p>We can now re-write our coercion function using malli:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">malli.core</span> <span class="ss">:as</span> <span class="nv">m</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">'</span><span class="p">[</span><span class="nv">malli.transform</span> <span class="ss">:as</span> <span class="nv">mt</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">coerce</span> <span class="p">[</span><span class="nv">data</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">m/decode</span> <span class="nv">Event</span> <span class="nv">data</span> <span class="nv">mt/json-transformer</span><span class="p">))</span>
</span></span></code></pre></div><p>That’s it. <code>mt/json-transformer</code> is a built-in transfomer that knows how to
decode instants and keywords from strings and how to coerce a vector into a set.
And now you have a schema for your data, which you can use for
<a href="https://github.com/metosin/malli#examples">validation</a>.</p>
<p>You don’t have to use Malli, but do yourself a favor and do not mix data
coercion logic with business logic.</p>
Karhunkierros IV
https://quanttype.net/p/karhunkierros-4/
Thu, 14 Nov 2019 00:00:00 +0000https://quanttype.net/p/karhunkierros-4/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/este_hu_4aee18bea2f1313.webp" type="image/webp" />
<img src="https://quanttype.net/images/este.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p><em>This post continues from where <a href="https://quanttype.net/posts/2019-11-07-karhunkierros-3.html">part 3</a> ended.</em></p>
<p>How did I get home from Ruka? I took a taxi to Kuusamo, took a bus to Oulu, had
decent palak paneer at Garam Masala right next to the railway station, and took
a train to Helsinki. It took me 12 hours in total. Boring and uneventful.</p>
<h2 id="cooking-with-andrew-skurka">Cooking with Andrew Skurka</h2>
<p>I read <a href="https://andrewskurka.com/blog/">Andrew Skurka’s blog</a> before the trip and got inspired.</p>
<p>I used his <a href="https://andrewskurka.com/gear-list-backpacking-stove-system-upright-canister-for-soloist-couples/">Fast & Light</a> stove setup for cooking: a tiny FireMaple gas
burner, a 0.9 litre pot, a 2.5 dl cup and a large plastic spoon. This is simpler
and lighter than the Trangia I’ve used before, but I didn’t miss anything. The
only downsides were that you couldn’t boil water at while you were eating and
the gas burner was a bit slow in the cold weather.</p>
<p>I also tried a couple of Skurka’s recipes:</p>
<ul>
<li><a href="https://andrewskurka.com/backpacking-dinner-idea-thai-peanut-noodles/">Peanut sauce &
noodles</a>
is great and a nice departure from “red hiking meal” genre. A friend tried it
out with her scout group. Apparently the taste was too weird and the recipe
was too vegetarian for Luvia teenagers. But I, with my more metropolitan
taste, will definitely use this recipe again.</li>
<li><a href="https://andrewskurka.com/backpacking-dinner-recipe-pesto-noodles/">Pesto noodles</a> wasn’t anything special. Not awful, but probably won’t use it again.</li>
<li><a href="https://andrewskurka.com/breakfast-recipe-oatmeal-with-fixings/">Oatmeal with fixings</a> was nice. It’s not exactly a unique idea, but I liked the Skurka version.</li>
</ul>
<p>I didn’t try his <a href="https://andrewskurka.com/backpacking-dinner-recipe-beans-rice-with-fritos-cheese/">beans + fries with Fritos &
cheese</a>
recipe because WTF even are Fritos, but apparently it’s so good that it’s an
Internet meme. Maybe next time.</p>
<p>I assumed that I need 3000 kcal per day and prepared 800 kcal portions for lunch
and dinner. This turned out to be too much. I just couldn’t eat such a large
amount of food in one go and had to start splitting the portions in half. Next
time I will go with 500 kcal meals and add more snacks to have enough calories.</p>
<p>My trip was one day shorter than expected, I split some of the portions,
and had one meal at a restaurant, Because of all of this, I’m confused about
whether the 3000 kcal estimate was right. It was in the right ballpark, at
least. The food weighted about 750 g per day.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/easy2_hu_bd9af9a9077f0b3a.webp" type="image/webp" />
<img src="https://quanttype.net/images/easy2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<h2 id="in-conclusion">In conclusion</h2>
<p>I’d like to do a bit longer trip the next time. It took me almost two days to
get in the proper hiking mood, but once I was in the mood, I could’ve continued
for a couple of days more. I’m going to look for a 120 km hikes the next time.</p>
<p>Would I recommend Karhunkierros? It’s a good destination for inexperienced
hikers like me:</p>
<ul>
<li>You can stay at the huts and the lean-to shelters.</li>
<li>Water is easily available from the river.</li>
<li>The trail is marked well – there’s no risk of getting lost.</li>
<li>The trail is easy to get to using public transport.</li>
<li>Great sights!</li>
</ul>
<p>The downside is that it’s very popular during the season. Off-season, the winter
is harsh, so you need to know what you’re doing.</p>
<p>I think it’s suitable for a 4-6 day hike for people of average fitness. You can
adjust the number of days based on your fitness, skill level, and how easily you
get bored when you’re not walking. If you’re in a hurry, take part in
<a href="https://nutskarhunkierros.fi/en/">NUTS Karhunkierros</a> trail running competition
and run it in a day.</p>
Karhunkierros III
https://quanttype.net/p/karhunkierros-3/
Thu, 07 Nov 2019 00:00:00 +0000https://quanttype.net/p/karhunkierros-3/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mutka2_hu_3a8793ee7676e620.webp" type="image/webp" />
<img src="https://quanttype.net/images/mutka2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p><em>This post continues from where <a href="https://quanttype.net/posts/2019-10-31-karhunkierros-2.html">part 2</a> ended.</em></p>
<h2 id="day-4---from-ansakämppä-to-siilastupa---22-km">Day 4 - from Ansakämppä to Siilastupa - 22 km</h2>
<p>People’s sleeping patterns at the huts were a mystery to me. I had been one of
the last to go to sleep and I was one of the first to wake up. People were going
to the bed already at eight. Me, the well-known night owl, stayed up until ten
and woke up just before seven. Many were still sleeping when I left the hut
after eight. I guess people just love to sleep?</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/naava_hu_6f84a60bb5ea5013.webp" type="image/webp" />
<img src="https://quanttype.net/images/naava.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/suo_hu_907f0dc90efbef45.webp" type="image/webp" />
<img src="https://quanttype.net/images/suo.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<p>My hiking speed was picking up, so I decided to re-evaluate my plans. Originally
the hike was supposed to take six days. This day, the fourth day, I’d camp at
some lean-to on the way to Pieni Karhunkierros. On the fifth day, I’d reach
Porontimajoki and then it’d be a short walk to Ruka on the sixth day. I decided
to speed things up and get to Siilastupa that day and finish the hike the next day.
Otherwise I’d have to spend too much time getting bored at huts.</p>
<p>The morning was uneventful and I got to Jussinkämppä quickly. Then I hit one of
the hardest sections of my hike. The route took me right to the riverside. In the
map, it looked like a flat and easy section and I thought I’d blaze through it.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jokiranta_hu_5bcfd742ccbd6c74.webp" type="image/webp" />
<img src="https://quanttype.net/images/jokiranta.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>In reality, the path was sloped and it was full of rocks and roots. Climbing
over them got tiring quickly. When the trail finally hugging the river, there
was steep uphill. I was so tired that I worried that I wouldn’t make it to the
hut.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joki1_hu_c3148bb57fc27d8a.webp" type="image/webp" />
<img src="https://quanttype.net/images/joki1.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joki2_hu_53a8e352d585c41a.webp" type="image/webp" />
<img src="https://quanttype.net/images/joki2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joutsen2_hu_78798dc215706e97.webp" type="image/webp" />
<img src="https://quanttype.net/images/joutsen2.jpg" width="600" height="600" loading="lazy" />
</picture>
</figure>
</p>
<p>After the hill, the path got easier. Soon I entered Pieni Karhunkierros, a
shorter trail that overlaps with Karhunkierros. It’s frequented by dayhikers and
the trail is in very good condition. It’s not quite as spectacular as its bigger
sibling – there’s so much forest that you can’t see much.</p>
<p>Just before the sunset I reached the Siilastupa hut. The hut is small and there
was already a couple with a dog and a German family, but the upper floor of the two-level group
bunk bed was still entirely free. The other guests were
feeding the fireplace eagerly and for the first time on the trip, it was too
warm.</p>
<h2 id="day-5---from-siilastupa-to-ruka---23-km">Day 5 - from Siilastupa to Ruka - 23 km</h2>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/koski_hu_6eec96d4dcaa8635.webp" type="image/webp" />
<img src="https://quanttype.net/images/koski.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>In the morning I chatted with the German dad. He apologized for snoring but
pointed out that I hadn’t been entirely quiet either. During the night, I kept rolling and rolling
around like I was in rotisserie and my pad is
<a href="https://www.instagram.com/p/BeaJDzPHu7B/">loud</a>. This inspired him to propose a
trail name for me: Kebab. I might pick it up if I ever to go hike in the US,
where they actually use trail names…</p>
<p>When I started walking, I got lost for the first time. The whitewater lured me
off the path to take a picture. After walking 20 meters, I realized that I wasn’t
seeing anyrail markings anymore and backed out. That’s how well the trail is
marked.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/konttainen_hu_202b5ce18d168ae5.webp" type="image/webp" />
<img src="https://quanttype.net/images/konttainen.jpg" width="600" height="401" loading="lazy" />
</picture>
</figure>
<p>The first 15 km of the day were easy. It was flat, uninspiring forest and marsh.
When the final 8 km started, shit got real. First there was a steep, small hill
called Konttainen.</p>
<p>I was worried when I was approaching it. It looked steep both in the map and in
the real life and to get to it, I had to scramble up rocky slopes packed with
ice. Luckily I was greeted by the sight of stairs at bottom of the hill.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/suo2_hu_7b30ba1344466675.webp" type="image/webp" />
<img src="https://quanttype.net/images/suo2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/portaat2_hu_f170adde038ab9f2.webp" type="image/webp" />
<img src="https://quanttype.net/images/portaat2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/huippu_hu_3ec3ea4968ce5351.webp" type="image/webp" />
<img src="https://quanttype.net/images/huippu.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<p>I walked up and down. After Konttainen, there’s Valtavaara. It’s steep <em>and</em> long with three peaks. This was the most
physically demanding section. You had to be careful to not slip when
ascending and descending the icy slopes. I fell over a couple of times but
did not hurt myself.</p>
<p>It felt good to reach the day hut on top of Valtavaara. There was fog, so there
weren’t any sights to mention, but making a cup of hot chocolate in the hut was a
welcome break.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/easy_hu_ff17040319791b10.webp" type="image/webp" />
<img src="https://quanttype.net/images/easy.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/easy3_hu_105ae5958c0a7f5e.webp" type="image/webp" />
<img src="https://quanttype.net/images/easy3.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/huippu2_hu_9f28f47fef2e6e02.webp" type="image/webp" />
<img src="https://quanttype.net/images/huippu2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
<p>After descending Valtavaara and seeing there was 1 km left, I was feeling pretty
jubilant already. I had forgotten that the final kilometer goes over
Rukatunturi. It was pretty disappointing to realize that the path goes up the
hill and under a ski lift.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ruka_hu_e40379f8ec01a276.webp" type="image/webp" />
<img src="https://quanttype.net/images/ruka.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>One more hill later and just before the sunset, I finally reached the end of the
trail. The gate of the trail is not in use, because they have dug out the stairs
to make space for a driveway. I scrambled up the hill to walk through the gate
anyway.</p>
<p>That was it. The day had been demanding and I could not have done another day
with so many hills. I was happy to have made it to Ruka.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ruka2_hu_af01e75601bd4ab6.webp" type="image/webp" />
<img src="https://quanttype.net/images/ruka2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I stayed night in the skiing resort hotel. Sauna felt good and the hotel
restaurant had a pretty decent vegan burger.</p>
<p><em>This is the third part of a series about hiking Karhunkierros. Read <a href="https://quanttype.net/posts/2019-11-14-karhunkierros-4.html">part 4</a>.</em></p>
Karhunkierros II
https://quanttype.net/p/karhunkierros-2/
Thu, 31 Oct 2019 00:00:00 +0000https://quanttype.net/p/karhunkierros-2/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/oulanka_hu_1a756d0324112d2a.webp" type="image/webp" />
<img src="https://quanttype.net/images/oulanka.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p><em>This post continues from where <a href="https://quanttype.net/posts/2019-10-24-karhunkierros.html">part 1</a> ended.</em></p>
<h2 id="day-2---from-perttumakoski-lean-to-to-taivalköngäs-hut---13-km">Day 2 - From Perttumakoski lean-to to Taivalköngäs hut - 13 km</h2>
<p>The night was cold and I woke up before the sunrise. My mornings tend to be
slow: I took my time packing up the very wet tent and making some porridge. A
hour and a half later, the sun had already risen and I started walking.</p>
<p>The trail follows the Oulankajoki river. It took me to a high hill on the side of
the river valley and to the first sight of the trail: the Rupakivi rock in
Oulankajoki. In photos, you usually see it from the flat angle, but because the
trail goes high, you approach the rock by descending a set of stairs.
You get to properly see how weirdly shaped the rock is.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rupakivi_hu_aaed3a1719c3e54a.webp" type="image/webp" />
<img src="https://quanttype.net/images/rupakivi.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Unfortunately there are a lot of trees and they prevent getting nice photos from
the hill. If you want to see it properly, you just have to go there!</p>
<p>Around the lunch time I reached the Savilampi wilderness hut. Before cooking a
lunch I climbed up on the north side of the Oulanka canyon. The ascend is steep
and you’ll likely come back the same route, so I recommend leaving your backpack
at the hut.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kanjoni_hu_a00b1cb2d05540cc.webp" type="image/webp" />
<img src="https://quanttype.net/images/kanjoni.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>The view was spectacular! This was my favorite sight along the trail. You do not
have to hike the whole Karhunkierros to see it – there’s a parking spot nearby.
It would make a nice dayhike.</p>
<p>I returned to the hut and made some lunch. At the hut I met one of the many
families with kids who were on an overnight trip to hike a section of the trail.
There were a couple of huskies, too. People love to hike with their dogs, it
seems.</p>
<p>The water from the river is potable after boiling. All the huts have gas stoves.
My friend Mäkipää borrowed me a <a href="https://www.flickr.com/photos/mkpaa/44876679332/in/album-72157671676293257/">tiny canister stove</a>. It’s small and
light and it took seemingly forever to boil a litre of water when outside and
snowing. In warmer weather, it’s probably great, but I wouldn’t want to take it
to any colder situations. I used the stoves at the huts as much as possible.</p>
<p>After lunch, I continued to the Taivalköngäs wilderness hut and arrived there
early at around 15. The distance for the day was short, but I was still a bit
shocked by the hike so I was happy to stop there. It’s a picturesque place: the
hut is next to little rapids and there’s a rope bridge across the river.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/taivalkongas2_hu_373582ac412a09bd.webp" type="image/webp" />
<img src="https://quanttype.net/images/taivalkongas2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>Again there were a couple of dads with their kids. Hikers kept arriving. We
had a full house for the night and one group of late arrivers even set up a tent
in the yard. In theory the people who have been at the hut the longest should
make room for the newcomers, but in practice nobody is going to get kicked out
in the middle of night.</p>
<h2 id="day-3---from-taivalköngäs-to-ansakämppä---17-km">Day 3 - from Taivalköngäs to Ansakämppä - 17 km</h2>
<p>Even though we were sleeping inside a hut, I had a cold night. I woke up grumpy
and with a headache. Probably it was because of a some combination of
dehydration, caffeine withdrawal, and a smoky hut.</p>
<p>I stepped out of the hut to get some water and was met by a surprise: there was
snow. It was a Full HD winter wonderland situation.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/taivalkongas_hu_b856f735f19e0c08.webp" type="image/webp" />
<img src="https://quanttype.net/images/taivalkongas.jpg" width="600" height="400" alt="Taivalköngäs hut covered in snow" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/taivalkongas3_hu_90126482a1692db0.webp" type="image/webp" />
<img src="https://quanttype.net/images/taivalkongas3.jpg" width="600" height="400" alt="Rope bridge going over Taivalköngäs" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/talvi3_hu_3f0fd683e659aee2.webp" type="image/webp" />
<img src="https://quanttype.net/images/talvi3.jpg" width="600" height="400" alt="Serene snowy scenery" loading="lazy" />
</picture>
</figure>
</p>
<p>After some porridge, I started walking. I could see from the fresh snow that I
was the first person to hike the section from Taivalköngäs to the next lean-to
shelter that morning. Only a squirrel and some reindeers had been there before
me. It was delightful, but still it was one of the hardest sections mentally.
After the poorly slept night I was in a really bad mood and even considered
giving up once I reach Oulanka Visitor Centre.</p>
<p>Once I finally got to the visitor centre, I decided to have a long lunch and
ordered a burger and a couple of cups of coffee. After eating, drinking, and
resting for two hours my mood had improved considerably. I started walking again and
finally got into the proper hiking state of mind, where you stop thinking about
how long it is left and start to just enjoy being there.</p>
<p>Right after the visitor centre there are the Kiutaköngäs rapids. It’s another
highlight of the route: a canyon with huge rocks and strong
whitewater. You can easily get there by a car and there were a lot of visitors
and dayhikers.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kiutakongas_hu_c18560860942343c.webp" type="image/webp" />
<img src="https://quanttype.net/images/kiutakongas.jpg" width="600" height="400" alt="Kiutaköngäs whitewater and canyon wall" loading="lazy" />
</picture>
</figure>
<p>After the rapids, the trail was flat and in great condition. I even reached my
top speed of 5 km/h. I made it easily to the next hut, Ansakämppä. There I had a
dinner and was considering continuing to Jussinkämppä. While I was washing
the dishes, a woman walked in and informed us that Jussinkämppä was going to be full
of people and dogs. People are fine, but five dogs is a bit much. I decided to
stay.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kuukkeli_hu_d9aa5fb9b68f8251.webp" type="image/webp" />
<img src="https://quanttype.net/images/kuukkeli.jpg" width="600" height="400" alt="A Siberian jay sitting in a spruce" loading="lazy" />
</picture>
</figure>
<p><em>This is the second part of a series about hiking Karhunkierros. Read <a href="https://quanttype.net/posts/2019-11-07-karhunkierros-3.html">part 3</a>.</em></p>
Karhunkierros I
https://quanttype.net/p/karhunkierros/
Thu, 24 Oct 2019 00:00:00 +0000https://quanttype.net/p/karhunkierros/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mark_hu_217030063a46be19.webp" type="image/webp" />
<img src="https://quanttype.net/images/mark.jpg" width="600" height="400" alt="A tree next to a trail. The tree has an orange mark painted on it." loading="lazy" />
</picture>
</figure>
<p>Last week I hiked the <a href="https://www.nationalparks.fi/en/karhunkierros">Karhunkierros trail</a>. To summarize, it was
dark and wet but not very cold.</p>
<p>Karhunkierros is a 82 km hiking trail in Northern Finland. The southern end of
the trail is at Ruka skiing resort in Kuusamo and the northern end is in a small
village called Hautajärvi in Salla. Usually people start at Hautajärvi.</p>
<p>I wanted to do my first longer solo hike and Karhunkierros seemed like a good
choice: the length is suitable for a week-long vacation and the trail is
well-marked and has good services. There’s a string of wilderness huts and
lean-to shelters and you can get to the trailhead by public transport.</p>
<p>Karhunkierros is the most popular hiking route in Finland. This has some
downsides. It’s cramped on-season and the trail has been worn out by the heavy
traffic. An upside is that if you get in trouble, there’s a good chance that
somebody will walk by.</p>
<p>October is off-season, in theory, except that I picked the autumn school holiday
week of Southern Finland. The Ruka slopes had been just opened and there were
dozens of skiers dayhiking and doing overnight trips. I stayed three nights in
huts and they were almost full every night. If you’re looking for some alone
time, you should go somewhere else.</p>
<h2 id="day-0---from-helsinki-to-oulu">Day 0 - from Helsinki to Oulu</h2>
<p>My trip got started on Sunday night. I had briely considered flying to Kuusamo,
but you can’t take gas canisters to the plane. Avoiding flying is good for the
climate anyway. Thus I went to Helsinki railway station, took <a href="https://mobile.twitter.com/arcatan/status/1183464999114760193">a photo in
Minuuttibaari</a>, and stepped on the night train to Oulu.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/marsh_hu_24500350d3d502bc.webp" type="image/webp" />
<img src="https://quanttype.net/images/marsh.jpg" width="600" height="400" alt="Duckboards going through a frozen, snow-covered marsh." loading="lazy" />
</picture>
</figure>
<h2 id="day-1---from-oulu-to-perttumakoski-lean-to--7-km">Day 1 - from Oulu to Perttumakoski lean-to – 7 km</h2>
<p>The next morning in Oulu, I took a bus to Kuusamo. There’s not much going on in
Kuusamo, but I had time to eat a vegan lunch at <a href="https://kasvisravintolakarpalo.fi">Karpalo</a>. According to
their home page is the most northern vegan restaurant in the world. Is it
really? I don’t know. The food was okay.</p>
<p>From Kuusamo, I took a bus to Hautajärvi. It’s marketed as the Karhunkierros
bus, but turns out it’s also the school bus for the local kids. There were me,
two couples with backpacks, and a dozen of 15-year-olds taking the
Karhunkierrobussi. The bus took me right next to the trailhead in Hautajärvi and
at around 16:00, I started walking.</p>
<p>The walk took me through some marshes where I first heard and then saw two
Siberian jays. To my surprise, they allowed me to get right next to them. Later
on I saw multiple groups feeding the jays – not so big surprise after all!</p>
<p>After seven kilometers of walking, I reached the Perttumakoski lean-to shelter
right before the sunset at 17:45. I felt that it’d be nice to have a bit more
privacy than a lean-to offers and so I pitched my tent. It became dark and it
started snowing while I was pitching it and it took me a while to get it right.
The tent has color-coded ribbons in the corners, but of course I forgot about
this. Pro-tip: if you plan to pitch a tent with the rain fly first so that the
inner tent keeps dry, better practice it at home.</p>
<p>I cooked a quick dinner, read a book for a couple of hours and started to sleep.</p>
<p>In the spring, I had bought a new synthetic three-season sleeping bag, Marmot
Trestles Elite Eco 20. This was the first night where I tried it anywhere close
to freezing. Even though it was barely freezing and the bag’s comfort rating is
about 0 °C, I woke up to put on more clothes a couple of times. Honestly, it was
disappointing. I’ll be on the market for a nice warm down bag.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/chilly_hu_1ccbbddc3c56899a.webp" type="image/webp" />
<img src="https://quanttype.net/images/chilly.jpg" width="600" height="400" alt="Trail going through slightly frozen forest." loading="lazy" />
</picture>
</figure>
<p><em>This is the first part of a series about hiking Karhunkierros. Read <a href="https://quanttype.net/posts/2019-10-31-karhunkierros-2.html">part 2</a>.</em></p>
The joys of coverage
https://quanttype.net/p/the-joys-of-coverage/
Thu, 10 Oct 2019 00:00:00 +0000https://quanttype.net/p/the-joys-of-coverage/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/moss2_hu_6f5fc1c5581b0d63.webp" type="image/webp" />
<img src="https://quanttype.net/images/moss2.jpg" width="600" height="400" alt="Close-up of a rock covered by moss" loading="lazy" />
</picture>
</figure>
<p>In the project I’m working on, we have been tracking the test coverage of a
Clojure web backend with <a href="https://github.com/cloverage/cloverage">Cloverage</a> and <a href="https://codecov.io">Codecov</a>. We use
Codecov’s ratcheting scheme where every pull request has to have as high
form coverage as the master branch.</p>
<p>We’ve had this setup for eight months and we’ve gotten a number of benefits out
of it:</p>
<ul>
<li>
<p>The coverage report helps you to check if your tests work correctly. Many
times I’ve thought that I’ve written a unit test to run all the code of
a function, but the report has revealed that there’s a branch that has not
been executed. Either the test is wrong, the code is wrong. In any case I am wrong.</p>
</li>
<li>
<p>The coverage report can help you to find dead code. If a private function
is not covered, it’s not needed for anything.</p>
</li>
<li>
<p>Sometimes I think that “this code is so simple that it does not need a test”,
but then Codecov complains and I write one anyway. These tests have found way
more bugs than I’d like to admit.</p>
</li>
</ul>
<p>Because Clojure is a dynamically typed language, even just trying to run your
code without checking the results can find bugs.</p>
<h2 id="the-downsides">The downsides</h2>
<p>In practice, we sometimes have to merge PRs even though they do not have high
enough coverage. Cloverage measures both the line coverage (which lines have
code that was executed) and form coverage (which Clojure forms were executed).
Getting high line coverage is straightforward, but getting full form coverage is
tricky:</p>
<ul>
<li>Sometimes getting full form coverage is impossible. Cloverage measures the
form coverage on macroexpanded code. Many macros expand to code with unreachable branches.
For example, consider an assertion:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">assert </span><span class="p">(</span><span class="nb">pos? </span><span class="nv">x</span><span class="p">))</span>
</span></span></code></pre></div>It expands to the following code:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nf">clojure.core/int?</span> <span class="nv">user/x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">do</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">throw</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">new</span>
</span></span><span class="line"><span class="cl"> <span class="nv">java.lang.AssertionError</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.core/str</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Assert failed: "</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">clojure.core/pr-str</span> <span class="o">'</span><span class="p">(</span><span class="nf">clojure.core/int?</span> <span class="nv">user/x</span><span class="p">)))))))</span>
</span></span></code></pre></div><p>The only way to get full form coverage for this code is to execute the both
branches of the <code>if</code> expression. That is, you need to get the assertion to
both pass <em>and</em> fail! But since a failing assertion means that <a href="http://wiki.c2.com/?WhatAreAssertions">there’s a bug
in the program</a>, the failure branch should be impossible to reach.</p>
<ul>
<li>Cloverage is implemented with side-effecting macros, but Clojure is
implemented in such a way that a macro call may be evaluated more than once
(see <a href="https://clojure.atlassian.net/browse/CLJ-1407">CLJ-1407</a>). This causes bugs where e.g. fully covered <code>loop</code>
and <code>doseq</code> forms are reported as partially covered.</li>
<li>We have a namespace which is too large to instrument with Cloverage.
Since Cloverage works by adding annotations to the code before evaluating it,
it can make the code size go over the “Method code too large” threshold.</li>
<li>There are some oddities and outright bugs in Codecov’s reporting, so you can’t
blindly trust their reports.</li>
</ul>
<h2 id="in-conclusion">In conclusion</h2>
<p>When we started tracking the coverage in February, our coverage was around 50%
and it has been floating around 80-85% for the last six months. For select parts
we’ve made the effort to maintain it over 90%.</p>
<p>I know some experienced developers believe that <a href="https://www.drmaciver.com/2015/04/some-empirically-derived-testing-principles/">100% coverage is
mandatory</a>. I want to believe it but I just don’t know how it should be
interpreted in the world of Clojure with all the problems! I’m also hesitant to
adopt this practice for open-source projects.</p>
<p>That said, Cloverage and Codecov are straightforward to set up if you already
are running your tests via a CI service. If you’re okay with dealing with
imperfect coverage measurements, I recommend trying it out.</p>
The hallway track conference
https://quanttype.net/p/hallway-track-conference/
Mon, 12 Aug 2019 00:00:00 +0000https://quanttype.net/p/hallway-track-conference/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/feest-bw_hu_ba01b5f3e39c34b9.webp" type="image/webp" />
<img src="https://quanttype.net/images/feest-bw.jpg" width="600" height="400" alt="A sign over a door that says "FEEST FEEST"" loading="lazy" />
</picture>
</figure>
<p>If you have ever talked to an experienced software developer about tech
conferences, you have probably heard them say something like this: <em>“Oh, I
mostly ignore the talks and focus on talking with people in the hallway.”</em></p>
<p>I’m one of those people. It’s a bit sad: the speakers have spent <a href="http://wunder.schoenaberselten.com/2016/02/16/how-to-prepare-and-write-a-tech-conference-talk/">a huge amount
of effort</a> to prepare their talk and we travel there from the other side
of the world to ignore them.</p>
<p>A week ago I attended the <a href="https://heartofclojure.eu">Heart of Clojure</a> conference in Leuven, Belgium.
What was so great about it was that they had plenty of time and space for the
hallway track and not too many talks<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>They had long breaks, including the extra-long siesta break in the middle of the
day, and non-talk activities such as a sketchnote workshop, meditation, and a
screen-printing workshop (I was <a href="https://twitter.com/arcatan/status/1157647881794478080">delighted</a>).</p>
<p>In the evening there was the Adventurous Dinner, where the participants were
randomly split into groups of eight or so people and sent to different
restaurants around the city center. This was great for meeting a new group of
people and chatting with them about the day. Afterwards we congregated to a
noisy bar for a more traditional conference after-party.</p>
<p>The result was that everybody was talking to each other. People made new friends
and seems like everybody was having a great time. There was a real feeling of
community. Often at conferences you have to be a speaker or otherwise an insider
to get the full experience. This time everybody got the insider experience.</p>
<p>I’d recommend the conference but understandably it looks like that <a href="https://lambdaisland.com/blog/2019-08-09-fork-this-conference">Heart of
Clojure won’t happen again</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Out of the talks I attended, my favorite was the keynote by <a href="https://twitter.com/Rachel_norfolk">Rachel Lawson</a>: <a href="https://slides.com/rachel_norfolk/opensource-about-code"><em>My opensource project; It’s all about the code</em></a>. So topical for the Clojure community! <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Interpreting Moriyama
https://quanttype.net/p/moriyama/
Wed, 24 Jul 2019 00:00:00 +0000https://quanttype.net/p/moriyama/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hiljaa_hu_31cbb239d9ec1cd1.webp" type="image/webp" />
<img src="https://quanttype.net/images/hiljaa.jpg" width="600" height="400" alt="Close-up of a metal door with a sticker saying SULJE PORTTI HILJAA" loading="lazy" />
</picture>
</figure>
<p>Daido Moriyama is one of the best-known Japanese photographers. So I read in his and Takeshi Nakamoto’s book <em>How I Take Photographs</em>.</p>
<p>His name did not ring any bells but I’ve seen <a href="https://www.moma.org/collection/works/52215">his photos</a> before. I doubt the photos in the book are his best work but at least the interviews are interesting. He talks about how he only takes snapshots.</p>
<p>Based on the book I couldn’t understand why he would be the most important Japanese photographer. Where’s the appeal? This inspired me to try to imitate his style. Here’s how I interpreted it:</p>
<ul>
<li>If you see something interesting, snap a photo.</li>
<li>If in doubt, snap a photo.</li>
<li>Go closer.</li>
<li>Impressions are more important than technical perfection.</li>
<li>Movement is okay; sharpness is not important.</li>
<li>All photography is copying, anyway. Photos of posters are fine.</li>
<li>It’s okay for the photographer to be visible.</li>
</ul>
<p><a href="https://vapaus.org/kallio/">Click here to view the resulting series.</a></p>
<p>The hardest commandment to follow for me was to go closer - especially since I shoot with <a href="https://quanttype.net/posts/2019-05-26-initial-impressions-ricoh-gr-iii.html">such a wide lens</a>. I’m going to keep practicing.</p>
Ricoh GR III - initial impressions
https://quanttype.net/p/initial-impressions-ricoh-gr-iii/
Sun, 26 May 2019 00:00:00 +0000https://quanttype.net/p/initial-impressions-ricoh-gr-iii/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hakone_hu_f9d190be99f5a793.webp" type="image/webp" />
<img src="https://quanttype.net/images/hakone.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I’ve got a new camera: Ricoh GR III. I got it when it first became available in
early April and since then I’ve shot about 2500 frames with it.</p>
<p>I haven’t explored it enough to offer any kind of conclusive review. Heck, I’ve
mostly shot in P mode with JPEG output with default settings! Still, I want to
share my initial impressions and some pictures I’ve taken with the camera.</p>
<p><em>A bit of background:</em> GR III is a fixed lens compact camera with a fast, 28
mm-equivalent lens. It’s a successor to GR II, which has cult following in the
street photography circles.</p>
<p><strong>Good.</strong> The build quality and the form factor is great. The pictures are very
sharp and the colors are nice (but maybe not as nice as Fujifilm’s). It fits
well into a pocket. The touch screen is great for choosing a focus point. It can
be charged over USB-C. The exposure compensation joystick is quick to use.</p>
<p><strong>Not so good.</strong> The battery life is so-so – get an extra battery! I get maybe
one day worth of travel photography out of one charge. The P mode skews too much
toward wide-open aperture for my taste. Manual focus is cumbersome (but there’s
the snap focus mode).</p>
<p>Autofocus is usually fast but it has problems with low-contrast scenes. There’s
a firmware update that promises to improve the performance. I’m deferring my
judgement until I’ve installed the update.</p>
<p>I’m carrying the camera with Peak Design’s Leash sling strap. To get the anchor
cords through the attachment holes in the camera body, you have to place them
just right and use enough force and <a href="https://www.youtube.com/watch?v=m8D-tEzTfLI">a piece of string</a> (you need to do this only once). Leash is light
and easy to adjust and slides well over my clothes, so I’m pretty happy with the
setup.</p>
<p>In summary, Ricoh GR III is a great pocketable choice for travel and everyday
camera if you’re okay with the battery life – and if the idea of fixed 28
mm-equivalent lens makes sense to you in the first place.</p>
<p><em>All photos in the post have been shot with Ricoh GR III.</em></p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hakone2_hu_2b1bfae1b2e0ec0e.webp" type="image/webp" />
<img src="https://quanttype.net/images/hakone2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/yaku2_hu_b8822a9c6c5bfaf.webp" type="image/webp" />
<img src="https://quanttype.net/images/yaku2.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/juuret_hu_7b7a4c5e495fe20a.webp" type="image/webp" />
<img src="https://quanttype.net/images/juuret.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/koysi_hu_670ebf1d0bfafb27.webp" type="image/webp" />
<img src="https://quanttype.net/images/koysi.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
</p>
Handbrewing coffee
https://quanttype.net/p/handbrewing-coffee/
Wed, 06 Mar 2019 00:00:00 +0000https://quanttype.net/p/handbrewing-coffee/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/barn_hu_f4a051cbbc4dcfcc.webp" type="image/webp" />
<img src="https://quanttype.net/images/barn.jpg" width="600" height="450" alt="A cup of black filter coffee on a wooden table in front of a window to a street." loading="lazy" />
</picture>
</figure>
<p>Shawn Blanc <a href="https://shawnblanc.net/2019/03/everything-requires-maintenance-2/">wrote</a> about how he switched to a Moccamaster for brewing
his morning coffee. It’s a nice post about productivity, but it made me want to
talk about coffee.</p>
<p>We hand-brew all<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> the coffee we drink at the office with a drip cone. We
have been doing this for a couple of years already. I like it, but out of
courtesy towards my colleagues I’ve floated the idea of buying an electric
coffee maker a couple of times. So far they have preferred to continue with
hand-brewing. But why bother?</p>
<p>It’s not because of the taste. I don’t think that I could tell apart my hand-brew
and well-made batch brew in a blind test.</p>
<p>I like the ritual, and the exercise in patience. First you weigh<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> and grind the
beans while waiting for the way-too-slow kettle to boil the water. Then you pour and wait and
pour and wait. Then you wash the cone and finally you get to taste the coffee.</p>
<p>The drip cone maintenance is easy, too. Washing the cone after each use
takes only a couple of seconds. The fundamentals of good coffee are
freshly-ground beans and clean equipment. Taking care of the latter couldn’t be
easier. And hand-brewing makes it easier to not drink too much coffee, because
the coffee is not there just waiting for you to overdose.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kalita_hu_d4d0487615f4c2b2.webp" type="image/webp" />
<img src="https://quanttype.net/images/kalita.jpg" width="600" height="515.5" alt="A Kalita filter cone filled with coffee grounds. The cone is on a mug and the mug is on a balance." loading="lazy" />
</picture>
</figure>
<p>On the other hand, using an electric coffee maker would free me from pouring the
water. I brew coffee maybe once per day, so that would take me about 3.5 minutes
a day. There are about 200 working days in a year, so that amounts to almost 12
hours. It takes something like six hours to read a medium-size novel. In a year
I could read two more books during my breaks instead of staring at coffee. The choices!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>We do have an espresso machine, but we use it maybe once a month. Also we buy beans that work great for filter coffee and turns out that not all of them are great for espresso. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>My standard recipe is 15 grams of coffee to 250 grams of water. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Revisiting Clojure testing
https://quanttype.net/p/revisiting-clojure-testing/
Tue, 29 Jan 2019 00:00:00 +0000https://quanttype.net/p/revisiting-clojure-testing/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/talvi2_hu_a872349a5883639d.webp" type="image/webp" />
<img src="https://quanttype.net/images/talvi2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>Two years ago I wrote about <a href="https://quanttype.net/posts/2017-01-26-clojure-test-runner-of-my-dreams.html">the Clojure test runner of my dreams</a>. Back
then, I asked for a Clojure test runner that would support clojure.test and that
would have the following features:</p>
<ul>
<li><strong>Output catching</strong> to make noisy test suites quiet.</li>
<li><strong>Test tagging</strong> for selectively running tests.</li>
<li><strong>JUnit output</strong> for integration with CI tools such as Circle.</li>
<li><strong>Test slowness reporting</strong> to speed up slow test suites.</li>
</ul>
<p>Let’s see where we are now.</p>
<p>After my post, <a href="https://github.com/weavejester/eftest">Eftest</a> soon got all the features. It’s a great library and while the
included Leiningen plugin is quite rudimentary,
<a href="https://github.com/metosin/bat-test">bat-test</a> offers a feature-rich wrapper
for use with Leiningen and Boot.</p>
<p>When Arne Brasseur <a href="https://lambdaisland.com/blog/2018-11-02-test-wars-new-hope">announced</a> that he is developing <a href="https://github.com/lambdaisland/kaocha">Kaocha</a>,
I didn’t see much point in a new test runner. However, I was working a new
project that used tools.deps as the dependency management tool. Since bat-test
does not have a <code>clj -m</code> -compatible version, I decided to give Kaocha a go.</p>
<p>After using Kaocha for a couple of months, I have to admit that Arne has created
a something worthwhile. Kaocha offers test running with all the above-mentioned
features and more (e.g. watcher, Cloverage integration) in one coherent,
well-documented package. Furthermore, Kaocha’s design <a href="https://cljdoc.org/d/lambdaisland/kaocha/0.0-389/doc/9-extending">allows easy
extensions</a>. For example, it was super-easy to create a plugin that
toggles on <a href="https://github.com/jeaye/orchestra">Orchestra</a> instrumentation.</p>
<p>If you’re looking for a new test runner, definitely check out Kaocha.</p>
<p><em>Side-note</em>: Arne’s work on Kaocha has been funded by <a href="https://www.clojuriststogether.org">Clojurists
Together</a>. Their funding has paid for a
number of improvements in Clojure projects <a href="https://www.clojuriststogether.org/projects/">you’re most likely
using</a>. The money comes from individual and corporate sponsors. It seems like a great way
for Clojure-using companies to fund work on the tools that their developers use.
I’m happy that Metosin has been a sponsor since the beginning and I’m hoping to
see more companies on <a href="https://www.clojuriststogether.org/members/">the member list</a>.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/talvi_hu_e5b390fd0c95ba57.webp" type="image/webp" />
<img src="https://quanttype.net/images/talvi.jpg" width="600" height="458.5" loading="lazy" />
</picture>
</figure>
<h2 id="whats-next">What’s next?</h2>
<p>Test runners are good now. Does this mean that the Clojure testing landscape is
perfect now? Well… I see at least two areas of improvement.</p>
<p><strong>ClojureScript unit testing</strong>. <a href="https://github.com/bensu/doo">Doo</a> (which I co-maintain) is tricky to set up,
I’ve had a number of problems with <a href="https://github.com/crisptrutski/boot-cljs-test">boot-cljs-test</a>, and <a href="https://quanttype.net/posts/2017-02-08-clojurescript-karma.html">using
Karma directly</a> is a lot of work. Getting my list of <em>dream features</em> to
work is possible but tedious.</p>
<p>Luckily there are two new developments: Figwheel Main has <a href="https://figwheel.org/docs/testing.html">built-in unit testing
support</a> and Kaocha has <a href="https://github.com/lambdaisland/kaocha-cljs">kaocha-cljs</a>. Both
look promising. This is what I’m most excited about Kaocha – I’m hoping that Kaocha manages to bring the same turn-key experience
to ClojureScript that it already has for Clojure.</p>
<p><strong>Assertion libraries.</strong> In clojure.test the assertion are written with the <code>is</code>
macro, which isn’t very expressive. The main selling point of <a href="https://github.com/marick/Midje">Midje</a> was
its powerful syntax for writing assertions. Unfortunately that power came with
the cost of a complex implementation.</p>
<p>Apart from writing your own predicates, what are the current options? I’m aware of <a href="https://github.com/metosin/testit">testit</a> (my go-to choice),
<a href="https://github.com/juxt/iota">iota</a>, and
<a href="https://github.com/nubank/matcher-combinators">matcher-combinators</a>. Each of
them is significant improvement over <code>is</code> macro, but I don’t love any of them.
I guess I need to come up with a list of features for the assertion library of
my dreams!</p>
Yearnote 2018
https://quanttype.net/p/yearnote-2018/
Tue, 01 Jan 2019 00:00:00 +0000https://quanttype.net/p/yearnote-2018/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/aulanko_hu_5f816b0b019d0402.webp" type="image/webp" />
<img src="https://quanttype.net/images/aulanko.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>It’s time to look back at 2018 and talk about <em>me</em>.</p>
<h2 id="on-being-a-professional">On being a professional</h2>
<p>The biggest thing for me personally was finishing <a href="http://urn.fi/URN:NBN:fi-fe201804208631">my master’s thesis</a>
and graduating as a Master of Science. I’m glad it’s finally done. After
graduating I continued working at <a href="https://www.metosin.fi/">Metosin</a> as a
software developer. In autumn, I joined Metosin’s board of directors.</p>
<p>At work, I took more responsibility on project management. It felt meaningful
even if it always wasn’t fun. In general, I feel like I’ve made more mistakes
lately. I reckon this is a good thing: either my job has become more challenging
or I’ve become better at recognizing mistakes. Both mean more learning.</p>
<p>Here are some things I learned from, other than making mistakes:</p>
<ul>
<li>Camille Fournier’s book <em>The Manager’s Path</em> helped me to understand how
engineering management works.</li>
<li>The <a href="./2018-07-05-lead-dev-2018.html">Lead Developer London</a> conference
was useful as well.</li>
<li>Zach Tellman’s <a href="https://leanpub.com/elementsofclojure"><em>Elements of Clojure</em></a>
and John Ousterhout’s <em>A Philosophy of Software Design</em> are good treatises
on software design in the small.</li>
</ul>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/aulanko2_hu_1007882dcb2420dd.webp" type="image/webp" />
<img src="https://quanttype.net/images/aulanko2.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<h2 id="on-having-a-life-outside-work">On having a life outside work</h2>
<p>These were fun:</p>
<ul>
<li>I started playing piano again after a decade-long break.</li>
<li><a href="https://quanttype.net/posts/2018-08-21-i-made-a-backpack.html">I made a backpack.</a></li>
<li><a href="./2018-01-06-yearnote-2017.html">A year ago</a> I hoped to hike and sail more and do more yoga.
My hiking plans fell apart, but I did have a great sailing trip in the summer
and practiced yoga almost every week!</li>
</ul>
<p>Some cool cultural works:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/On_Body_and_Soul"><em>On Body and Soul</em></a> was a great, subtle movie.</li>
<li>N. K. Jemisin’s <a href="https://en.wikipedia.org/wiki/N._K._Jemisin#Broken_Earth_series"><em>Broken Earth</em> trilogy</a> is solid and topical. Recommended to anyone who reads sci-fi.</li>
</ul>
<p>What about my plans for 2019?</p>
<ul>
<li>I’m planning to level up my <em>thoughtleader</em> game this year, so expect more
blog posts. I’m back to a situation where it would be sustainable to
consistently blog.</li>
<li>I’m giving <a href="https://clojured.de/schedule/#miikkaKoskinen">a lightning talk</a> at
the :clojureD conference in Berlin in February. If you’re attending, come to
say hi! :)</li>
<li>I hope to give a full-length talk at some other conference later
this year. Working on it!</li>
</ul>
<p>While 2018 was a pretty okay year for me, I know it was a hard year for a lot of
people both on the personal and the societal level. Frankly, I’m not feeling optimistic about the politics. It will get worse before it gets better.</p>
<p>Finally, a year ago I wrote this:</p>
<blockquote>
<p>I can’t believe it’s 2018 and Juha Sipilä’s cabinet still hasn’t
fallen apart.</p>
</blockquote>
<p>Unfortunately it’s 2019 and Sipilä’s cabinet still hasn’t fallen apart, but at
least the parliamentary elections are coming up in April.</p>
How I use tap>
https://quanttype.net/p/how-i-use-tap/
Thu, 18 Oct 2018 00:00:00 +0000https://quanttype.net/p/how-i-use-tap/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/silta_hu_fbc860d5fa7519a7.webp" type="image/webp" />
<img src="https://quanttype.net/images/silta.jpg" width="600" height="450" alt="A shadow against a bridge pillar on a rock." loading="lazy" />
</picture>
</figure>
<p>One of the <a href="https://github.com/clojure/clojure/blob/master/changes.md#changes-to-clojure-in-version-110">new features in Clojure 1.10</a> is tap. The changelog
describes it as follows:</p>
<blockquote>
<p>tap is a shared, globally accessible system for distributing a series of
informational or diagnostic values to a set of (presumably effectful) handler
functions. It can be used as a better debug <code>prn</code>, or for facilities like
logging etc.</p>
<p><code>tap></code> sends a value to the set of taps. Taps can be added with <code>add-tap</code> and will
be called with any value sent to <code>tap></code>. The tap function may (briefly) block
(e.g. for streams) and will never impede calls to <code>tap></code>, but blocking
indefinitely may cause tap values to be dropped. If no taps are registered,
<code>tap></code> discards. Remove taps with <code>remove-tap</code>.</p>
</blockquote>
<p>I’m already using it as a better debug prn! I’m using <a href="https://cursive-ide.com">Cursive</a> and I
connect to a REPL launched by <a href="http://boot-clj.com">Boot</a>. With my setup, <code>(prn :DEBUG value)</code> has two potential downsides.</p>
<ol>
<li>The output may go either to the Boot terminal or to the Cursive IDE depending
on the code path.</li>
<li>The output is not pretty-printed.</li>
</ol>
<p>Tap allows me to solve both problems. I want my debug prints to always appear
in Cursive’s REPL, so after starting the REPL, I add a tap handler by running
this command:<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">add-tap</span> <span class="p">(</span><span class="nf">bound-fn*</span> <span class="nv">puget.printer/cprint</span><span class="p">))</span>
</span></span></code></pre></div><p>Here <a href="https://clojuredocs.org/clojure.core/bound-fn*">bound-fn*</a> ensures that
the output goes to Cursive and not to the terminal. <a href="https://github.com/greglook/puget">Puget</a> is the
pretty-printer I’m used to, but you can replace it with your favorite printer.
If you do not want to add new deps, you can use <code>clojure.pprint</code> or even plain
old <code>prn</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">add-tap</span> <span class="p">(</span><span class="nf">bound-fn*</span> <span class="nv">clojure.pprint/pprint</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">add-tap</span> <span class="p">(</span><span class="nf">bound-fn*</span> <span class="nv">prn</span><span class="p">))</span>
</span></span></code></pre></div><p>Now when I want to debug-print something, I do <code>(tap> "hello world")</code>. Since
both <code>tap></code> and <code>add-tap</code> are in <code>clojure.core</code>, I don’t need to require
anything. I can just <code>tap></code> away.</p>
<p>Another debugging trick is to store the tapped value in an atom. I’ve used this
only once so far, but it was pretty handy. Setup:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">debug-a</span> <span class="p">(</span><span class="nf">atom</span> <span class="nv">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">add-tap</span> <span class="o">#</span><span class="p">(</span><span class="nf">reset!</span> <span class="nv">debug-a</span> <span class="nv">%</span><span class="p">))</span>
</span></span></code></pre></div><p>Now I can <code>tap></code> an intermediate value in the middle of some complicated code
and then poke at it in the REPL via <code>@debug-a</code>. Ideally you’d use
a <a href="https://github.com/razum2um/clj-debugger">debugger</a>, but if you are in hurry, maybe tap is enough.</p>
<p><em>Thanks to Wade Mealing for feedback on this post.</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><del>I don’t know how to do this automatically. If somebody knows, please tell me.</del> You can do it automatically by adding it to <a href="https://clojure-goes-fast.com/blog/system-wide-user-clj"><code>user.clj</code></a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Clojure libraries I recommend
https://quanttype.net/p/clojure-libraries-i-use-and-like/
Sun, 14 Oct 2018 00:00:00 +0000https://quanttype.net/p/clojure-libraries-i-use-and-like/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/syksy_hu_1853104a5d23ec0e.webp" type="image/webp" />
<img src="https://quanttype.net/images/syksy.jpg" width="600" height="450" alt="Brown maple leafs on the ground." loading="lazy" />
</picture>
</figure>
<p>Every now and then I see people asking what Clojure and ClojureScript libraries
they should be using. In this post, I’ll share my list of libraries for building full-stack
Clojure/ClojureScript web applications. I’ve been building this kind of applications for a while now
and I believe you can rely on these
libraries.</p>
<p>Here’s what I’m looking for:</p>
<ul>
<li><em>Robustness.</em> I don’t want to debug buggy libraries – I write enough bugs of my own.</li>
<li><em>Extensiblity.</em> With a long-lived application, you’ll end up wanting all kind
custom things that the library authors didn’t anticipate. Data-driven design
helps here.</li>
<li><em>Implemetation is easy to understand</em>. Eventually I’ll read the source anyway.
Either there is a bug or there is no documentation.</li>
</ul>
<p>This list is not meant as the final truth: there are plenty of good libraries
out there and you might choose different ones based on your needs and
preferences. There’s an obvious bias, too: many of these libraries were made by
my colleagues at Metosin.</p>
<h2 id="clojure-backend">Clojure backend</h2>
<p><strong>Framework.</strong> I appreciate the data-driven nature of
<a href="https://github.com/weavejester/integrant">Integrant</a>. Combine it with
<a href="https://github.com/weavejester/integrant-repl">Integrant-REPL</a> for reloaded
workflow.</p>
<p><strong>JDBC database connection pool.</strong> I’ve never had any problems with
<a href="https://github.com/brettwooldridge/HikariCP">HikariCP</a> and it’s easy to
configure and instrument. <a href="https://github.com/tomekw/hikari-cp">hikari-cp</a> is a
handy Clojure wrapper.</p>
<p><strong>Configuration.</strong> I use <a href="https://github.com/metosin/maailma">Maailma</a> and I’ve
written about <a href="https://www.metosin.fi/blog/configuring-clojure-apps/">how to use
it</a>.
<a href="https://github.com/tolitius/cprop">cprop</a> is very similar and probably a bit
more powerful.</p>
<p><strong>HTTP routing.</strong> I like the data-driven nature of
<a href="https://github.com/metosin/reitit">reitit</a>. It’s fast, too, and has enough
extension points so that anything you want is possible. I would <em>not</em> use
<a href="https://github.com/metosin/compojure-api">compojure-api</a> because the
implementation is way too hard to understand.</p>
<p><strong>Logging.</strong> I use <a href="https://github.com/ptaoussanis/timbre">Timbre</a> because it’s
so easy to write custom appenders for it. Admittedly I haven’t checked out the
popular Java options.</p>
<p><strong>Test runner.</strong> <a href="https://github.com/weavejester/eftest">Eftest</a> is the <a href="https://quanttype.net/posts/2017-01-26-clojure-test-runner-of-my-dreams.html">test
runner of my dreams</a> and you
can’t go wrong by using it. At some point I want to give
<a href="https://github.com/lambdaisland/kaocha">Kaocha</a> a go.</p>
<p><strong>Test assertion library.</strong> I like <a href="https://github.com/metosin/testit">testit</a>
and Juxt’s <a href="https://github.com/juxt/iota">iota</a>. They’re adequate, but I think
there’s room to do better here.</p>
<p><strong>Test data generation.</strong> I only just started using
<a href="https://github.com/reifyhealth/specmonstah">specmonstah</a> for generating graphs
of test data, but I’m excited. It’s so much nicer than writing the equivalent
code by hand. There’s a bit of learning curve, though.</p>
<h2 id="clojurescript-frontend">ClojureScript frontend</h2>
<p><strong>Build tool</strong>. <a href="https://figwheel.org">Figwheel</a> works very well and its getting
better all the time. I’d either use it with Leiningen or directly with <code>clj</code>.</p>
<p><strong>Front-end framework.</strong> <a href="https://github.com/Day8/re-frame">re-frame</a> is the way
to go. Check out <a href="https://github.com/Day8/re-frame-10x">re-frame-10x</a> for
debugging.</p>
<p><strong>Translations.</strong> <a href="https://github.com/ptaoussanis/tempura">Tempura</a> works for
me. I use a small Clojure tool for extracting the translatable strings into <a href="https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html">a
PO file</a>
for editing with <a href="https://poedit.net">Poedit</a>. The tool is proprietary right now
but I hope to open source it.</p>
<h2 id="general-tools">General tools</h2>
<p><strong>clojure.spec helpers.</strong> <a href="https://github.com/bhb/expound">Expound</a> makes the
spec error messages human-readable and
<a href="https://github.com/jeaye/orchestra">Orchestra</a> instruments the function return
values.</p>
<p><strong>Data manipulation utilities.</strong> I use the combination of
<a href="https://github.com/metosin/potpuri">Potpuri</a>,
<a href="https://github.com/cgrand/xforms">xforms</a>, and
<a href="https://github.com/nathanmarz/specter">Specter</a>. Potpuri is an old-fashioned
“missing parts of clojure.core” library and xforms is the same for transducers.
Specter is the closest thing to lenses for Clojure. It’s powerful but takes some
time to learn. I recommend trying it out for some simple tasks – soon you’ll see
opportunities for it everywhere.</p>
<h2 id="what-about-">What about …?</h2>
<p>I haven’t included any SQL libraries, HTTP clients or servers, or async
libraries, because I don’t have a clear recommendation for any of these
(important!) categories.</p>
I made a backpack
https://quanttype.net/p/i-made-a-backpack/
Tue, 21 Aug 2018 00:00:00 +0000https://quanttype.net/p/i-made-a-backpack/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/reppu1_hu_66a3600f33649dcd.webp" type="image/webp" />
<img src="https://quanttype.net/images/reppu1.jpg" width="600" height="428" loading="lazy" />
</picture>
</figure>
<p>For a long time, I used a <a href="https://scandinavianoutdoor.com/haglofs/gear/backpacks-and-bags/backpacks/tight-evo-xl/">Haglöfs Tight Evo XL</a> as my everyday
backpack. It was not optimal: it’s a bit too large for my everyday needs, but a
bit too small for extended trips. It’s not too stylish, either. However, I
didn’t want to spend money on a new backpack. Luckily there is a simple
solution: <em>make your own gear</em>.</p>
<p>My main inspiration was the <a href="http://sandiegomitch.com/backpacking.html">DIY IKEA backpack</a> which is made out of <a href="https://www.ikea.com/us/en/catalog/products/17228340/">IKEA
shopping bags</a>. We didn’t have any spare IKEA bags, but there was a
worn-out <a href="https://www.clasohlson.com/uk/Shopping-Bag-M/70-104-1">Clas Ohlson bag</a> which is made of similar material. My girlfriend’s
colleague gave me a piece of parachute cutting waste and a broken Haglöfs
backpack for scavenging webbing and buckles. I was set for materials.</p>
<p>I came with a list of design constraints:</p>
<ul>
<li>Should have enough space for a 15" laptop and headphones, but not much else.</li>
<li>Needs to have a convenient place for a bicycle U-lock.</li>
<li>Must look cool!!</li>
<li>Should be simple enough so that I can actually make it.</li>
</ul>
<p>I realized that I have no idea of what I’m doing. In this kind of situations my
usual solution is to copy from others. Thus I started by taking measurements
from a <a href="https://www.fjallraven.com/shop/fjallraven-kanken-F23510/">Kånken</a> and modifying them to accommodate a 15" laptop.</p>
<p>I wanted to have a top-loading pack and zippers seemed expensive and tricky to
sew, so I opted to have a drawcord closure with a lid on top. I also added open
pockets to the front and the sides. I don’t like limp packs, so I added an
internal pocket for a framesheet. I drew up a pattern and found out that there’s
just enough fabric for a one bag. Perfect!</p>
<p>At first I thought that I would make the shoulder straps out of the webbing
scavenged from the broken backpack. That didn’t work because there wasn’t enough
of it. Then I realized that I could just use the old backpack’s shoulder straps
as-is and get comfy straps for free.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/reppu2_hu_e489e9ae834a0579.webp" type="image/webp" />
<img src="https://quanttype.net/images/reppu2.jpg" width="600" height="435" loading="lazy" />
</picture>
</figure>
<p>It took me one Saturday to make most of the pack and then a couple of evenings
to finish it. I did end up buying a cordlock, some cord, and a piece of
cardboard to be used as a framesheet. Total budget: about 6 €.</p>
<p>I made the pack in May and I’ve been using it almost daily ever since. I’m
really happy how well it turned out. The size is just right and I like the
looks. The side pockets are great for the U-lock and as a bonus feature, the
framesheet pocket is great for transporting stacks of paper.</p>
<p>I’ve had to fix it a couple of times. I didn’t know how to attach the shoulder
straps and as a result, they’re falling off. Unfortunately it’s hard to fix
without taking the pack apart. The material wasn’t as sturdy as I thought. It
should have been folded for reinforcement in the places with most stress.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/reppu3_hu_811623c94345c416.webp" type="image/webp" />
<img src="https://quanttype.net/images/reppu3.jpg" width="600" height="435.5" loading="lazy" />
</picture>
</figure>
<p>Anyway, making a backpack was fun, easy, and rewarding. I’ve made a pair of
pants before, but making clothes is hard because they need to actually fit you.
With bags, the exact fit hardly matters.</p>
<p>I’m not much of a maker, but it’s great to make something concrete every now and
then. It also made me appreciate the high quality of factory-made backpacks
more – they might be worth the money after all.</p>
Fully automated releases
https://quanttype.net/p/fully-automated-releases/
Sat, 11 Aug 2018 00:00:00 +0000https://quanttype.net/p/fully-automated-releases/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sailing_hu_50233da8937a23b8.webp" type="image/webp" />
<img src="https://quanttype.net/images/sailing.jpg" width="600" height="462.5" loading="lazy" />
</picture>
</figure>
<p>Have you ever contributed a patch to an open-source project, got it merged, and
then waited months for a new release that would include your patch? Me too,
reader, I’ve been there.</p>
<p>Moreover, I’ve been the maintainer who hasn’t gotten around to cutting a
release. Cutting releases is a chore. Usually it’s <a href="https://github.com/metosin/compojure-api/blob/master/docs/release-checklist.md">a fragile, multi-step
process</a>
that is not especially fun.</p>
<p>As programmers, what is our answer to fragile, multi-step processes? <em>We automate them.</em></p>
<h2 id="how-to-do-it">How to do it</h2>
<p>When creating a release, there are a couple of steps where human input and human judgement is needed.</p>
<ol>
<li>When to create a release?</li>
<li>How much should the version number be incremented?</li>
<li>What to write in the change log?</li>
</ol>
<p>There’s an automation-enabling answer to the first question that is familiar for many developers from their work environment: embrace <em>continuous delivery</em> and <em>continuous deployment</em>. Each pull request should leave the project in a state where it can be released. Then you can automatically create a release every time you merge a pull request.</p>
<p>You still need a human to answer the second and third question, at least if you have a conventional versioning scheme. To move the burden away from the maintainer, you can ask the contributors to fill in this information during the contribution process.</p>
<p>I know of two actual implementations of this: <a href="https://hypothesis.works/articles/continuous-releases/">Hypothesis continuous release process</a> and <a href="https://github.com/semantic-release/semantic-release">semantic-release</a>. Hypothesis asks you to include a special file in your pull requests that looks like this:</p>
<pre><code>RELEASE_TYPE: minor
This release adds a function for printing the text "Hello, world!".
</code></pre>
<p>semantic-release relies on <a href="https://github.com/semantic-release/semantic-release#commit-message-format">specially-formatted commit messages</a>:</p>
<pre><code>feat(core): add function for printing ”Hello, world!”
</code></pre>
<p>Here <code>feat</code> means this commit adds a new feature, implying a minor release if you’re following <a href="https://semver.org/">Semantic Versioning</a>.</p>
<p>In both cases, after a pull request has been successfully merged, the CI server will read this information, update the change log, increment the version number, and push a new release to the package manager. As a contributor this means that if your patch gets merged, it will be released.</p>
<h2 id="what-about-clojure">What about Clojure?</h2>
<p>I’m not aware of anyone doing this in the Clojure community<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> , but I believe it would be beneficial. There are a lot of small projects that would get contributions but the maintainers are not around to merge and release them. Automated releases would make the work of the existing maintainers easier and it would also make it simpler to onboard new maintainers.</p>
<p>I have implemented a proof-of-concept version of the Hypothesis process for <a href="https://github.com/miikka/cache-metrics">cache-metrics</a>, a small library of mine, but I haven’t yet dared to introduce it to any ”real” libraries. Many actively developed projects would need to change their ways of working as you couldn’t just merge or commit random things to master.</p>
<p>I hope this post acts a starting point for a discussion. What do you think?</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="http://github.com/cljsjs/packages">cljsjs/packages</a> kind-of does it, but it is a a package repository and not a library. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Migrated to Hugo
https://quanttype.net/p/migrated-to-hugo/
Sun, 05 Aug 2018 00:00:00 +0000https://quanttype.net/p/migrated-to-hugo/<p>After five years of using <a href="https://jaspervdj.be/hakyll/">Hakyll</a> to generate this site from a bunch of
Markdown files, I finally migrated to <a href="https://gohugo.io">Hugo</a>. Hakyll is pretty cool, but
using it makes only sense if you’re using Haskell for other stuff as well. I
haven’t touched Haskell lately. Alas. Hugo has everything I need built-in and
seems to work well enough.</p>
<p>I’ve tried to keep all the important URLs same or add redirects when that is not
possible. Feed readers might do something weird with the updated feed, though.
Sorry about that. If something is broken, please let me know.</p>
Why interceptors?
https://quanttype.net/p/why-interceptors/
Fri, 03 Aug 2018 00:00:00 +0000https://quanttype.net/p/why-interceptors/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kallahdenniemi_hu_568324e2063788e.webp" type="image/webp" />
<img src="https://quanttype.net/images/kallahdenniemi.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
<p>At Metosin, we have been thinking about <em>interceptors</em> in Clojure and
ClojureScript. We’ve thought about them so much that in fact we made our own
interceptor library for Clojure, called
<a href="https://github.com/metosin/sieppari">Sieppari</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. To understand why, let’s
take a look at the pros and cons of using interceptors instead of middleware.</p>
<p>Interceptors are a Clojure pattern, <a href="http://pedestal.io/reference/interceptors">pioneered by the Pedestal
framework</a>, that replaces the
<a href="https://github.com/ring-clojure/ring/wiki/Concepts#middleware"><em>middleware</em>
pattern</a> used by
Ring. In Pedestal, they are used for handling HTTP requests, but they can be used for handling all kinds of requests.
For example in <a href="https://github.com/Day8/re-frame/blob/master/docs/Interceptors.md">re-frame</a>
they’re used in handling web frontend events such as button clicks.</p>
<p>At Metosin, we’ve used them in a bunch of projects and we’re developing Sieppari
to be used with <a href="https://github.com/metosin/reitit">reitit</a>, our (latest) HTTP
routing library.</p>
<h2 id="lets-work-this-out">Let’s work this out</h2>
<p>In Ring, a HTTP request handler is a function that takes a request map and returns a response map. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">my-handler</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="ss">:status</span> <span class="mi">200</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:headers</span> <span class="p">{</span><span class="s">"Content-Type"</span> <span class="s">"text/plain"</span><span class="p">}</span>,
</span></span><span class="line"><span class="cl"> <span class="ss">:body</span> <span class="s">"hello!!"</span><span class="p">}))</span>
</span></span></code></pre></div><p>To enhance the behavior of the handler in reusable way, you can wrap it with a higher-order function that takes the handler as parameter. This can be used to implement features like <a href="https://github.com/metosin/muuntaja">content encoding and decoding</a> and <a href="https://github.com/cemerick/friend">authentication</a>.</p>
<p>For example, here’s a debugging middleware that prints the incoming request map and the outgoing response map:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">print-middleware</span> <span class="p">[</span><span class="nv">handler</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">request</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:REQUEST</span> <span class="nv">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">response</span> <span class="p">(</span><span class="nf">handler</span> <span class="nv">request</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:RESPONSE</span> <span class="nv">response</span><span class="p">))))</span>
</span></span></code></pre></div><p>The good thing about middleware is that they’re simple to implement: it’s just a Clojure function and you can use standard constructs such as try-catch. They’re fast, too.</p>
<p>The problems start when you try handle asynchronous operations. <a href="https://github.com/ring-clojure/ring/blob/master/SPEC">Ring specifies</a> async handlers as functions that take callbacks for sending a response and raising an exception as arguments. We have to write a separate version of our debugging middleware for asynchronous handlers.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">debug-middleware</span> <span class="p">[</span><span class="nv">handler</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">request</span> <span class="nv">respond</span> <span class="nv">raise</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:REQUEST</span> <span class="nv">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">handler</span> <span class="nv">request</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">response</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:RESPONSE</span> <span class="nv">response</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">respond</span> <span class="nv">response</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nv">raise</span><span class="p">)))</span>
</span></span></code></pre></div><p>Using the same code for the synchronous and asynchronous handler is tricky and error handling gets difficult.</p>
<p>Interceptors offer a solution: you split the middleware in two phases<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, <code>:enter</code> and <code>:leave</code> (or <code>:before</code> and <code>:after</code> as they’re called by re-frame). <code>:enter</code> is called before executing the handler, <code>:leave</code> is called afterwards. Both phases get a context map as a parameter and they return an updated context map. The request is under the key <code>:request</code>
and the handler’s response is put under <code>:response</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">debug-interceptor</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="ss">:enter</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[{</span><span class="ss">:keys</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span> <span class="ss">:as</span> <span class="nv">context</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:REQUEST</span> <span class="nv">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">context</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:leave</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">fn </span><span class="p">[{</span><span class="ss">:keys</span> <span class="p">[</span><span class="nv">response</span><span class="p">]</span> <span class="ss">:as</span> <span class="nv">context</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">prn </span><span class="ss">:RESPONSE</span> <span class="nv">response</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">context</span><span class="p">)})</span>
</span></span></code></pre></div><p>Middleware can be composed by nesting function calls. With interceptors that
does not work, so you need to have an executor that takes a chain of
interceptors (called a queue) and executes them in order.</p>
<p>A cool thing you can now do is that if your interceptor returns an asynchronous result (a deferred or core.async channel for example), the executor can wait for it, and if the interceptor returns a synchronous result, the executor can act on it directly. This allows you to use the same interceptors for synchronous and asynchronous operations. The downside is that the executor is bound to be slower than nested function calls.</p>
<p>Another downside is that structures like try-catch and <code>with-open</code> do not work
anymore. To allow proper error handling, interceptors have an optional <code>:error</code>
phase that gets called if any of the inner interceptors throws an exception.</p>
<h2 id="the-queue-as-data">The queue as data</h2>
<p>Middleware do not <em>have</em> to call the handler. For example, an authorization
middleware may decide that a request is not authorized and instead of calling
the handler, it returns an error response.</p>
<p>Interceptors go further: the remaining queue and the stack of the
already-entered interceptors are exposed in the context map and you can
manipulate them. If your authorization middleware wants to return early, you can
<code>(assoc context :queue [])</code>. Another example is that you can have a routing
interceptor that pushes route-specific interceptors and a handler to the queue.</p>
<p>Finally, since your interceptor chain is now data instead of middleware-nesting code, you can do fancy tricks like dynamically re-order interceptors based on the dependencies between them. <a href="https://github.com/oliyh/angel-interceptor">angel-interceptor</a> is an implementation of this and Sieppari supports it as well. I’m a bit skeptical about whether there are real use cases for this, but it’s there if you need it.</p>
<h2 id="summary">Summary</h2>
<ul>
<li>Interceptors allow easy mixing of synchronous and asynchronous code.</li>
<li>Interceptors expose the queue and call stack as data, which gives you a fine-grained control over the execution.</li>
<li>Interceptors prevent you from doing error handling with try-catch – not that it would work well with asynchronous code anyway.</li>
<li>Interceptors are probably a bit slower than middleware.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>It’s not yet ready for production. At the time of writing, the latest release is <code>0.0.0-alpha5</code>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>You can do this without interceptors, of course. See e.g. how <a href="https://github.com/ring-clojure/ring/blob/369798ed93091210743d370886b28ee3801d87ef/ring-core/src/ring/middleware/session.clj#L107-L115">the session middleware</a> is implemented in the Ring codebase. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Lead Developer London 2018
https://quanttype.net/p/lead-dev-2018/
Thu, 05 Jul 2018 00:00:00 +0000https://quanttype.net/p/lead-dev-2018/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/leaddev_hu_9d0d13bff5f043e2.webp" type="image/webp" />
<img src="https://quanttype.net/images/leaddev.jpg" width="600" height="337.5" alt="The opening of The Lead Developer London conference." loading="lazy" />
</picture>
</figure>
<p>Last week, I attended <a href="https://london2018.theleaddeveloper.com/">The Lead Developer conference</a> in London. It’s a
conference about being a technical lead. The talks were a mix of general
leadership advice (it’s all about feedback!), tech lead specifics (focus on the
high-impact stuff in code reviews!), tech (ReasonML is cool!), and self-help
(remember to exercise and meditate!).</p>
<p>I decided to attend the conference because I’ve ended up taking on some tech
lead responsibilities and I’ve felt that I don’t know what I’m doing. Spending
two days listening to talks about tech leadership seemed like a good idea.</p>
<p>The talks repeated the fundamentals: getting and giving good feedback is a key
activity, you need to have empathy, and you need to be able to articulate why
you’re doing things in addition to how. The hard part, of course, is putting
these ideas in action.</p>
<h2 id="the-most-interesting-talks">The most interesting talks</h2>
<p>I have this taxonomy of conference talks that I like. It subjective since it
depends on what you already know. Here it is anyway:</p>
<ol>
<li>Clarifying talks. These do not necessarily teach you new things, but they
make you see the things you already know more clearly and connect them
to other ideas.</li>
<li>Inspiring talks. These make you <em>want</em> to do things and do them well.</li>
<li>Toolbox talks. These talks give you actionable ideas to use in your work.
Great if the ideas are new to you, potentially boring otherwise.</li>
<li>Tour talks. These talks walk you through a topic by covering a lot of ground
with way too many details in way too little time. The audience might get
confused, but <a href="https://quanttype.net/posts/2016-02-23-please-give-demanding-tech-talks.html">I like that</a>.</li>
</ol>
<p>Here are my highlights from the conference in each of the categories.
Unfortunately the videos haven’t been published yet.</p>
<p>Clarifying talks:</p>
<ul>
<li>Alex Hill’s talk on code review. She talked about how defensiveness can get in
the way of effective code reviews and what to do to avoid it. <a href="https://docs.google.com/presentation/d/1c0ntzST1VEEE0bZNn7kss9vOWpYJadVtwYMIJZkJRWg/edit">See
Alex’s slides</a> and <a href="http://www.alexandra-hill.com/2018/06/25/the-art-of-giving-and-receiving-code-reviews/">her blog post</a>. My <a href="https://quanttype.net/posts/2015-09-07-commandments-for-code-review.html">commandments for code
review</a> were inspired by similar ideas.</li>
</ul>
<p>Inspiring talks:</p>
<ul>
<li>Tara Ojo’s talk on mentoring junior developers. I’ve heard so many stories
about early-career people getting hired but then forgotten by the company.
Without help and guidance, there’s a big chance that they won’t succeed and
then they either leave or get fired. This is waste of everybody’s time. <a href="https://www.slideshare.net/TaraOjo/juniornextleaddeveloper">See
Tara’s slides</a>, she has good suggestions on what to do.</li>
<li>Alicia Liu’s talk on how to navigate your job when you get promoted to a new
management position and have no idea of what you’re doing.</li>
</ul>
<p>Toolbox talks:</p>
<ul>
<li>Kevin Goldmiths’s talk on techniques for 1:1s, team meetings, etc. I haven’t got
much experience with these, but his methods sounded good. <a href="https://www.slideshare.net/kevingoldsmith/agile-techniques-for-lead-developers/">See Kevin’s slides</a>.</li>
</ul>
<p>Tour talks:</p>
<ul>
<li>Jenny Duckett’s talk on how to build sustainable teams. There was way too much
information for me to process in one go, but what stuck with me was that you
and your team should make a habit of sharing information about your work
frequently to prevent the silo-ing of knowledge. For example, actually write
down <em>why</em> you decided to do something. <a href="https://noti.st/jennyd/bnnoWW/slides">See Jenny’s slides</a>.</li>
</ul>
<h2 id="on-the-event">On the event</h2>
<p>Usually the conferences I go to have been organized by people who are not
professional event organizers. The Lead Developer conference is organized by an
<a href="https://www.whiteoctoberevents.co.uk">event organizing company</a> and it showed in a good way: everything was
smooth.</p>
<p>My only complaint is about the venue. The lobby was so noisy during the breaks
that it was hard for me to hear what people were saying. This obviously makes
having a conversation hard and so I didn’t get much of talking to people.
Otherwise the Barbican Arts Centre was a pretty good venue. Even though there
were something like 1100 attendees, it didn’t feel crowded at all.</p>
<p>Some nice touches that would be cool to see at other conferences:</p>
<ul>
<li>Live captioning. All the talks were captioned live: next to the big screen,
there were smaller screens where the stenographer’s text was projected.
Obviously this helps the people with hearing difficulties, but it’s great for
everybody. If you lose the context for a second or if the speaker uses weird
words, you can look at the captioning and get back on track.</li>
<li>Speaker office hours. Instead of a Q&A session after each talk, there was a
room where you could meet the speakers during the break after their talks and
ask them questions. The one-on-one conversations were interesting and you
didn’t have endure any “this is more of a comment than question” takes.</li>
<li>They played kick-ass music when the speakers entered the stage. Really gave me
feeling that we’re now going to hear from an amazing person. I assume it made
the speakers feel cool, too.</li>
<li>They had a quieter chill-out area. There were guided mindfulness sessions, and
while I’m not into guided meditation myself, the sessions ensured a peaceful
atmosphere.</li>
</ul>
<p>In general, I’d recommend the conference if you’re the kind of person who likes
conferences, you’re interested in this topic, and your employer foots the bill.</p>
A night in Nuuksio
https://quanttype.net/p/a-night-in-nuuksio/
Tue, 15 May 2018 00:00:00 +0000https://quanttype.net/p/a-night-in-nuuksio/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/nuuksio3_hu_a7e5209a15584900.webp" type="image/webp" />
<img src="https://quanttype.net/images/nuuksio3.jpg" width="600" height="400.5" alt="My hammock by the lake. The cliff on the other side of lake is great for broadcasting your opinions to all the campers." loading="lazy" />
</picture>
</figure>
<p>Last summer, inspired by <a href="https://www.youtube.com/watch?v=f84n5oFoZBc">Rich Hickey</a> and the general trends, I bought a hammock. I didn’t end up using it much: I only slept half a night in it. I had pitched it in the attic of a summer cottage, but there was no sleeping pad or underquilt. Even indoors the convection was so bad that my butt froze. I spent the rest of night in a bed.</p>
<p>Still, I wanted to get a full night’s sleep in the hammock outdoors! I don’t have a tarp and <a href="https://en.wikipedia.org/wiki/Sleeping_bag#Temperature_ratings">the EN13537 lower limit</a> for my sleeping bag is 9 degrees Celsius (basically it’s a summer-only sleeping bag) so the weather would need to be perfect: warm, no rain, not too much wind.</p>
<p>Last weekend, the weather forecast promised exactly that! The night temperature would be balmy 8 degrees Celsius, the sky would be clear and there wouldn’t be any wind. I’ve never spent a night alone in the forest, but it was time to cross that off my bucket list.</p>
<p>I left home in the evening and after taking a metro, a train, and a bus, and hiking a couple of kilometers, I arrived to the northern Iso-Holma campsite in Nuuksio. It was already past eight o’clock and the sun would set at 21:48. I set up my hammock by the nice little lake and cooked some dinner.</p>
<p>Turns out that the a Trangia lid (at least <a href="https://quanttype.net/posts/2017-08-07-hiking-from-pyha-to-luosto.html">a one that got bent</a>) sucks for frying eggs. The eggs is stick to it and cook unevenly. I forgot the spatula home and the one I improvised from a piece of wood sucked as well. A lesson learned for the next time: either bring a spatula or learn to carve.</p>
<p>After the dinner, I settled in the hammock to read and to listen to the birds. There were cuckoos, woodpeckers, and woodcocks. I heard some raptors that I couldn’t identify and a black grouse, which I haven’t recognized before! A couple of whooper swans visited the lake, but luckily they didn’t spend the night there – they were extremely loud. And in the morning I heard what must have been <a href="https://youtu.be/jofNR_WkoCE">a fox</a>. All in all, it was a pretty good night for observing wildlife by ear.</p>
<p>Camping in Nuuksio on a warm spring weekend is not exactly a unique idea. In the national park, you’re only allowed to camp on the designated camping sites and each one I passed was full of people. I’m not sure if it counts as being alone when there are dozen tents in your vicinity. In addition to the birds, I got to listen to a computer science student praising CS and weed on the other side of the lake.</p>
<p>Sleeping in the hammock was… okay. While I didn’t freeze, my sleeping bag was clearly a bit too cold for the weather. They say that you should sleep in a slight angle in the hammock to avoid the banana shape. It was hard to get the inflatable sleeping pad nicely in that angle, so I ended up as a banana anyway. Next time I will try a foam pad – it’s more practical anyway.</p>
<p>Still, it was cool to sleep under the stars<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. I’ve always felt that you <em>have</em> to have a tent to sleep in the forest, but that’s just not true.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I didn’t see any stars. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Edit clipboard contents in Vim
https://quanttype.net/p/pbedit/
Mon, 07 May 2018 00:00:00 +0000https://quanttype.net/p/pbedit/<p>Wouldn’t it be handy to be able to edit the contents of the clipboard in a text
editor? Yes, it would, or at least I do it all the time. For example, I do it
when I want to copy text from one website to another website, but I need to
reformat it a bit first. For macOS, I have a script called <code>pbedit</code>. It is
super-simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">pbpaste <span class="p">|</span> vipe <span class="p">|</span> pbcopy
</span></span></code></pre></div><p><code>vipe</code> is a small program that launches <code>$EDITOR</code> and allows you to edit the
data piped between two programs. It’s part of
<a href="https://joeyh.name/code/moreutils/">moreutils</a>, which Homebrew users can
install with <code>brew install moreutils</code>. <code>pbpaste</code> and <code>pbcopy</code> are the built-in
macOS command-line tools for pasting and copying the clipboard.</p>
<p>Try this, or if you’re a Linux user, fashion its equivalent with <code>xclip</code>. Soon
you’ll find yourself using it all the time.</p>
How to write a talk proposal
https://quanttype.net/p/writing-talk-proposals/
Mon, 19 Mar 2018 00:00:00 +0000https://quanttype.net/p/writing-talk-proposals/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jaa_hu_52d04e1455b5c762.webp" type="image/webp" />
<img src="https://quanttype.net/images/jaa.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>There’s a tech conference or a meet-up coming up and you want to give a talk.
Thus you will have to write a proposal with a title and a description for your
talk. What should go into it?</p>
<p>Let’s recap the goals of your proposal. There are at least two:</p>
<ol>
<li>Convince the event organizers to pick your talk.</li>
<li>Convince the event attendees to come to your talk.</li>
</ol>
<p>The goals are well-aligned: the organizers want to have a line-up of talks that
makes the potential attendees <em>excited</em>.</p>
<p>You will need to do some selling and I’ll leave that part up to you. That said,
there’s some basic information that you should always include. When I’m reading
a proposal, I’m always trying to answer these three questions:</p>
<ol>
<li><strong>What is this talk about?</strong> This includes <em>how</em> you will talk about it.
For example, if you’re going to talk about a technology, I want know to if
you’re going to give a conceptual overview or do a deep dive into hairy
details, or something else.</li>
<li><strong>Why should I learn about this?</strong> It’s cool to learn about new stuff, but
I’m not going to be excited about your talk if I don’t have a clue about why
it’d be interesting for me.</li>
<li><strong>Who is this talk for?</strong> If I’m completely new to the topic, can I still get
something out of your talk? Or if I’m an expert, will I immediately get
bored?</li>
</ol>
<p>There’s no magical recipe for the perfect talk proposal, but addressing these
questions should get you started. Keep in mind that there are plenty of “right”
answers. For example, <em>it’s fun</em> is a legitimate answer for the second question
for many events.</p>
<hr>
<p>A lot has been written about this topic. If you want to read more,
I recommend starting with these two links:</p>
<ul>
<li>Lena Reinhard’s very comprehensive article <a href="http://wunder.schoenaberselten.com/2016/02/16/how-to-prepare-and-write-a-tech-conference-talk/">How to prepare and write a tech
conference talk</a>. She uses a similar list of questions!</li>
<li>The We Are All Awesome page on <a href="http://weareallaweso.me/for_speakers/how-to-write-a-compelling-proposal.html">How to write a compelling proposal</a>.</li>
</ul>
Name this conversation pattern
https://quanttype.net/p/conversation-pattern/
Mon, 22 Jan 2018 00:00:00 +0000https://quanttype.net/p/conversation-pattern/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ranta_hu_d9a76c2754f42d47.webp" type="image/webp" />
<img src="https://quanttype.net/images/ranta.jpg" width="600" height="357.5" loading="lazy" />
</picture>
</figure>
<p>There’s an annoying conversational anti-pattern for which I’d like to have a
name. It goes like this:</p>
<ul>
<li>Person A makes an argument.</li>
<li>Person B disagrees with the argument.</li>
<li>Person A assumes that person B simply didn’t understand the argument properly
and re-states it more elaborately.</li>
</ul>
<p>The crux is that it does not occur to person A that person B could legitimately
disagree with them. This leads to frustrating discussions for both sides.
Sometimes this is done deliberately to derail conversations, but I’ve seen
people do it in good faith.</p>
<p>I associate this pattern with highly-privileged people arguing with marginalized
people, especially if the highly-privileged person is committed to the status
quo but at the same time wants to be seen as an ally. You can see this happening
on Twitter every now and then.</p>
<p>I can’t believe I’m the only one noticing this pattern. If you know a good name
for it, please let me know (<a href="mailto:[email protected]">e-mail me</a> or <a href="https://twitter.com/arcatan">tweet at
me</a>).</p>
Yearnote 2017
https://quanttype.net/p/yearnote-2017/
Sat, 06 Jan 2018 00:00:00 +0000https://quanttype.net/p/yearnote-2017/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/reflect_hu_98d271e704ee1203.webp" type="image/webp" />
<img src="https://quanttype.net/images/reflect.jpg" width="600" height="400" alt="A log in a lake in Nuuksio. Out of my photos in 2017, this is my favorite." loading="lazy" />
</picture>
</figure>
<p>It’s January, so it’s time to look both at the past and at the future! Here’s
some good stuff that happened to me in 2017:</p>
<ul>
<li>I <em>finally</em> graduated as a BSc in mathematics and made good progress towards graduating as a MSc.</li>
<li>I had a relaxing summer vacation that included <a href="https://quanttype.net/posts/2017-08-07-hiking-from-pyha-to-luosto.html">hiking</a> and <a href="https://quanttype.net/posts/2017-05-27-on-sailing.html">sailing</a>.</li>
<li>I attended ICFP. I’ve wanted to go there for ages and finally had the chance. It was the most interesting conference I’ve been to.</li>
<li>In the fall, I started practicing ashtanga yoga. It turned out to be a good combination of exercise and light meditation.</li>
</ul>
<p>My thinking about software development evolved in small but important ways:</p>
<ul>
<li>Nowadays I’m more concerned about whether our team does good work than whether I do good work personally. Might be a sign of <a href="https://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/">maturing as an engineer</a>.</li>
<li>I no longer care about Haskell. Being a Haskell programmer used to be a part of my ego, but it’s time to admit that I don’t find Haskell interesting anymore. ICFP made this clear. I’m over being a <em><programming language X></em> person.</li>
</ul>
<p>Here’s what I hope 2018 will bring along:</p>
<ul>
<li>More good things: graduating as a MSc, more hiking, sailing, and yoga! More time outdoors!</li>
<li>I’d like to figure out how to publish writing regularly and sustainably. Blogging every week <a href="https://quanttype.net/posts/2017-09-14-break-from-blogging.html">didn’t work for me</a>, so I stopped it. I did continue to write, but I didn’t publish anything – that wasn’t useful either.</li>
<li>In general, it’d be cool to ship more things. I’ve always been good at understanding things, but it’s not useful unless you somehow reflect that understanding back to the world.</li>
</ul>
<p>I enjoyed some things in 2017:</p>
<ul>
<li>The best book I read was Anna Karenina by Leo Tolstoy. It covers universal themes like love, marriage, death of a loved one, and birth of a children. I felt that Tolstoy did a good job describing how differently various characters feel about the same events. I recommend it to everyone!</li>
<li><a href="https://goodlifecoffee.fi">Good Life Coffee’s</a> Kayon Mountain was excellent, we drank a lot of it at the office.</li>
<li><a href="http://www.tampereenkauppahalli.fi/kauppiaat/sushibarumami">Umami</a>, the sushi place in Tampereen kauppahalli, is great! I’m not a sushi connoisseur, but it’s the best sushi I’ve had in Finland.</li>
</ul>
<p>Finally, I can’t believe it’s 2018 and Juha Sipilä’s cabinet still hasn’t fallen apart.</p>
Secure Scuttlebutt: some technical details
https://quanttype.net/p/secure-scuttlebutt/
Sat, 30 Dec 2017 00:00:00 +0000https://quanttype.net/p/secure-scuttlebutt/<p>I’ve poked a bit at <a href="http://scuttlebutt.nz">Secure Scuttlebutt</a> (SSB). It’s a gossip protocol for syncing append-only cryptographically verified feeds. Its main application is social networking – I recommend <a href="https://www.scuttlebutt.nz/getting-started.html">giving Patchwork a go</a> if you want to see it in action.</p>
<p>The protocol is mostly defined by <a href="https://github.com/ssbc/scuttlebot">the implementation</a>, which is an archipelago of tiny Node.js modules. To make it easier for the next person trying to figure this out, let me give you a rough overview of the outer layers of the protocol:</p>
<ul>
<li>All the cryptographic operations are performed with <a href="https://download.libsodium.org/doc/">libsodium</a>.</li>
<li>The connection starts with a <a href="http://dominictarr.github.io/secret-handshake-paper/shs.pdf">Secret Handshake</a>. It is used to authenticate the connecting parties and to agree on session keys. It’s implemented in the <a href="https://github.com/auditdrivencrypto/secret-handshake">secret-handshake</a> module.</li>
<li>After the handshake, everything is encrypted with the session keys using the framing documented and implemented in the <a href="https://github.com/dominictarr/pull-box-stream">pull-box-streams</a>.</li>
<li>The encrypted content consists of <a href="https://github.com/ssbc/muxrpc">muxrpc</a> commands and data.</li>
<li>Concretely it’s a stream of packets encoded with the <a href="https://github.com/dominictarr/packet-stream-codec">packet-stream-buffers</a> module.</li>
</ul>
<p><strong>Edit</strong>: <em>For more, check out the <a href="https://vltf.org/scuttlebuttprotocolguide/index.html">Scuttlebot Protocol guide</a>. It looks super-infromative, but I didn’t know about it before writing this post! Thanks to André Staltz for pointing me to it.</em></p>
<p>I toyed around implementing SSB in <a href="https://www.ponylang.org">Pony</a> and this is how far I got. I suppose it’d be simple to implement a client that connects to a <a href="https://github.com/ssbc/scuttlebot">Scuttlebot</a> server to publish a message.</p>
<p>I don’t how much it’d take to implement a full-blown SSB node. At least you’d need the feed synchronization. I <em>think</em> it’s implemented by the <a href="https://github.com/ssbc/ssb-friends">ssb-friends</a> module, but I’m not 100% sure.</p>
A new JSON library appears
https://quanttype.net/p/json/
Thu, 21 Dec 2017 00:00:00 +0000https://quanttype.net/p/json/<p>At Metosin, we’ve released the first version of our own JSON library for Clojure, <a href="https://github.com/metosin/jsonista">jsonista</a>. I wrote about it <a href="http://www.metosin.fi/blog/faster-json-processing-with-jsonista/">on Metosin’s blog</a>.</p>
<p>If you’re a JSON enthusiast, you might want to check out <a href="https://www.tbray.org/ongoing/When/201x/2017/12/14/RFC-8259-STD-90">Tim Bray’s post on the new RFC 8259</a>. He calls it “the last specification of JSON that anyone will ever publish”.</p>
Break from blogging
https://quanttype.net/p/break-from-blogging/
Thu, 14 Sep 2017 00:00:00 +0000https://quanttype.net/p/break-from-blogging/<p>I’m going to take a break from blogging on quanttype. I haven’t enjoyed writing
the posts lately and the quality has suffered accordingly. There’s no point in
forcing it.</p>
<p>The future of blogs that go on hiatus is always uncertain. I’m not going to make
any promises.</p>
What is first-order logic?
https://quanttype.net/p/first-order-logic/
Mon, 28 Aug 2017 00:00:00 +0000https://quanttype.net/p/first-order-logic/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/puut_hu_c7e003ccd024fed8.webp" type="image/webp" />
<img src="https://quanttype.net/images/puut.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>I want to tell you about descriptive complexity theory, but it’s hard to explain
if you don’t know what first-order logic is. Let’s have a tiny refresher.</p>
<hr>
<p>Even if you haven’t heard about first-order logic, you’re probably familiar with it
anyway. It’s the basic language of logic with the following elements:</p>
<ul>
<li>logical connectives ∧ for conjunction (AND), ∨ for disjunction, and ¬ for
negation (NOT)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>existential quantifier $\exists$ and universal quantifier $\forall$</li>
<li>equality symbol $=$</li>
</ul>
<p>Using this language, you can talk about a domain which is a set of values. It’s
like having a database and writing queries against it using the first-order
logic as the query language. For example, the statement $\exists x (\exists y
(\neg (x = y)))$ asserts that there at least two distinct values in the domain.</p>
<p>First-order logic, abbreviated FO, is not very powerful. You can make it more
powerful by adding some extra predicates. For example, if you want to talk about
natural numbers, you could add the predicates + and <. Then you can state theorems like $\forall x (\forall y (x \leq x + y))$. The resulting logic is called FO(+,<).</p>
<p>FO is called <em>first-order</em> logic, because the existential and the universal
quantifiers quantify over atomic values. In second-order logic (SO), you’re
allowed to quantify over predicates. In third-order logic, you’re allowed to
quantify over predicates of predicates etc. This gives you a lot of leverage.
For example, in SO you can assert that the domain contains even number of
elements. This is not expressible in FO.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The exact set of connectives varies by the source, but it does not really
matter. Having ∧, ∨, and ¬ is enough, because you can express all the other
logical connectives in terms of them. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Please publish changelogs
https://quanttype.net/p/please-publish-changelogs/
Mon, 21 Aug 2017 00:00:00 +0000https://quanttype.net/p/please-publish-changelogs/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/log_hu_e4126a973b5d007c.webp" type="image/webp" />
<img src="https://quanttype.net/images/log.jpg" width="600" height="400" alt="Signs of changes in a log." loading="lazy" />
</picture>
</figure>
<p>When you release a new version of your library, please do a favor to your users
and publish a changelog entry highlighting the most important changes.</p>
<p>The changelog tells your users what’s new – it gives them a reason to upgrade.
It tells them what’s broken, so they won’t be surprised when nothing works
anymore.</p>
<p>I’ve heard this quip that the changelog is one of the most important pieces of
documentation, because even if the other documentation is lacking, it tells you
what is outdated about the knowledge you have discovered yourself.</p>
<p>Sure, the commit history is always there, but usually it’s hard to understand.
It’s easier to write a passable changelog than to curate the history.</p>
<hr>
<p>How to do it in
practice? <a href="http://keepachangelog.com/en/1.0.0/">keep a changelog</a> has elaborate
instructions. If you want to follow them, that’s great, but as long as you use
a consistent format with version numbers and release dates, I’m happy.</p>
Write more macros
https://quanttype.net/p/write-more-macros/
Mon, 14 Aug 2017 00:00:00 +0000https://quanttype.net/p/write-more-macros/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/metsa_hu_2afe7cfabdd682cb.webp" type="image/webp" />
<img src="https://quanttype.net/images/metsa.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>If you’re a Clojure programmer, it’s likely that you don’t write many macros.
Everybody is always warning against writing too many macros. Those people are
wrong: macros are great and you should write more of them.</p>
<p>Here are some good uses for macros:</p>
<ol>
<li><a href="https://quanttype.net/posts/2017-04-20-doto-do.html">Control structures keep your code simple</a>.</li>
<li><code>with-</code> macros keep your resource usage tidy.</li>
</ol>
<p>I dislike <code>def</code> macros, because I often need the corresponding non-def function
macros and I usually have to read the source to figure out how they differ. That
said, they’re okay when they look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">declare </span><span class="nv">foo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defmacro </span><span class="nv">deffoo</span> <span class="p">[</span><span class="nv">x</span> <span class="o">&</span> <span class="nv">args</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">`</span><span class="p">(</span><span class="k">def </span><span class="o">~</span><span class="nv">x</span> <span class="p">(</span><span class="nf">foo</span> <span class="o">~@</span><span class="nv">args</span><span class="p">)))</span>
</span></span></code></pre></div><p>You could even write a macro for defining <code>def</code> macros!</p>
Hiking from Pyhä to Luosto
https://quanttype.net/p/hiking-from-pyha-to-luosto/
Mon, 07 Aug 2017 00:00:00 +0000https://quanttype.net/p/hiking-from-pyha-to-luosto/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/isokuru_hu_566218631dd21b98.webp" type="image/webp" />
<img src="https://quanttype.net/images/isokuru.jpg" width="600" height="400" alt="A pond in the Isokuru gorge" loading="lazy" />
<figcaption>A pond in the Isokuru gorge.</figcaption>
</picture>
</figure>
<p>Last week I hiked in the <a href="http://www.nationalparks.fi/en/pyha-luostonp">Pyhä-Luosto national park</a> for four days. This was
my first multi-day hike ever and it was great! I’m not going to post a
full-blown travelogue, but I’ll try to make some notes about what made it great.</p>
<p><em>On terminology: I’m out of my depth with all these nature words. It’s hard to
properly map them from Finnish to English. Figuring out the proper terms for
wetlands is especially hard, so I’ll just call them mires. Sorry.</em></p>
<p>There are many reasons for hiking, but I do it because I enjoy beautiful
landscapes. Pyhä-Luosto delivers: it has fells and mires. They are my favorite
Finnish landscape elements. This was reflected in our pace. We walked only 10-12
km per day to have time for admiring and photographing the nature, to take
detours, to cook and in general just to not be in a hurry.</p>
<p>Hiking in Pyhä-Luosto is easy: the routes are well-marked and all the rest spots
are well-equipped. The huts even have gas stoves for cooking. This suited me
well as I’m not much of a bushcraft person. The only thing that required some
consideration was potable water. This is not the part of Lapland with brooks
everywhere. There’s water in the mires, for sure, but the idea of drinking it
didn’t exactly thrill me.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pyhalatva_hu_caca038630bb2c6c.webp" type="image/webp" />
<img src="https://quanttype.net/images/pyhalatva.jpg" width="600" height="400" alt="Duckboards in the Pyhälatva mire" loading="lazy" />
<figcaption>Duckboards in the Pyhälatva mire.</figcaption>
</picture>
</figure>
<h2 id="recommendations">Recommendations</h2>
<p>If you’re planning to hike in the Pyhä-Luosto national park, here are my
recommendations:</p>
<ol>
<li>Go see the Isokuru gorge. Its main sight is the
Pyhänkasteenputous waterfall and it’s one of the most picturesque places I’ve
visited in Finland.</li>
<li>If you like mires, walk the Luosto nature trail (vaellusluontopolku). It goes
through the Pyhälatva mire, showcasing different kinds of mire vegetation. If
you’re there in the right time of year, you might find a lot of cloudberries,
too. Unfortunately for us, they weren’t ripe yet.</li>
<li>If you’re going to climb on Ukko-Luosto, the south-east trail walks amidst
beautiful rocks, shrubs, and brush. I would go up the south-east trail and
take the stairs on the north-east side to get down.</li>
<li>The Lampivaara café has excellent home-made donuts.</li>
</ol>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/noitatunturi_hu_8fe2f658afbcdfc8.webp" type="image/webp" />
<img src="https://quanttype.net/images/noitatunturi.jpg" width="600" height="400" alt="This is why they tell you to use good shoes" loading="lazy" />
<figcaption>This is why they tell you to use good shoes.</figcaption>
</picture>
</figure>
<h2 id="lessons-for-the-next-hike">Lessons for the next hike</h2>
<p>I had some packing problems. I’ll try to do better next time.</p>
<ol>
<li>Use a backpack that can handle weight of your equipment. My Haglöfs internal
frame backpack is a travel model, but it was supposed to withstand hiking.
This turned out to be not true: I loaded it with some 20 kg of clothes,
equipment, food, and water. The frame got bent and on the second day of the
hike part of the frame popped out through two layers of fabric. We managed to
fix it, but I don’t want to load it fully again.</li>
<li>Maybe do not pack a Trangia stove sideways in the backpack. The lid of the
stove was circular when we started the trip. Now it’s oval. I don’t know
what happened.</li>
<li>Go on a practice walk with the fully-packed backpack. I hadn’t used this
backpack before with over 15 kg load. It took me a while to figure out how to
adjust it properly for the heavy loads. I could have done it closer to home.</li>
<li>Blocky or cylindrical water bottles would make packing easier. I used two 1.5
liter soft drink bottles to carry the water. The pointy tops of the bottles
made it hard to pack them efficiently.</li>
<li>Bring enough cocoa to have hot chocolate every day. Self-explanatory.</li>
<li>Bring a backup battery for the camera. The X100T battery won’t last for four
days.</li>
</ol>
Summer vacation
https://quanttype.net/p/summer-vacation/
Mon, 17 Jul 2017 00:00:00 +0000https://quanttype.net/p/summer-vacation/<p>I’m going on vacation and this blog will be on vacation as well. The regularly
scheduled musings on programming and stuff will continue in August.</p>
Focus on understandble code
https://quanttype.net/p/understanding-first/
Tue, 11 Jul 2017 00:00:00 +0000https://quanttype.net/p/understanding-first/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hammock_hu_72516cdd0e93ed4.webp" type="image/webp" />
<img src="https://quanttype.net/images/hammock.jpg" width="600" height="337.5" alt="It's hammock time?" loading="lazy" />
</picture>
</figure>
<p>When you review code, what do you focus on?</p>
<p>I focus on understading the code. My rule is that in any project with a
long-term team, all members should be able to understand almost all of the code.</p>
<p>If it’s hard for me to understand some code, it’s hard for me to change it or
debug it. Moreover, if it’s hard for me to understand it now, it’s likely hard
for the author to understand it once they’ve forgotten the details. Complicated
code is better at hiding its bugs, too.</p>
<p>Sometimes there are parts that require so much magic and advanced trickery
that it’s not reasonable to expect others to understand it without serious
study. Working around third-party bugs sometimes creates this kind of code, as
does micro-optimization. Usually these should be only a tiny fraction of your
code base.</p>
<p>This is why understandable code has such a high priority for me. It makes
the code easier to work with, for you and for everybody else, right now and in
the future.</p>
<p>This is one of those situations where the more junior developers can provide
valuable feedback for the more senior developers. If you’re a senior developer
and you wrote some code that the junior developer on your team has hard time
understanding, it’s time to do a reality check: is your code complicated
for a reason, or did you just mess it up?</p>
What is pair programming like?
https://quanttype.net/p/pair-programming/
Tue, 04 Jul 2017 00:00:00 +0000https://quanttype.net/p/pair-programming/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sorsat2_hu_b1ebece40e0298ed.webp" type="image/webp" />
<img src="https://quanttype.net/images/sorsat2.jpg" width="600" height="407" alt="A pair of mallards. Likely not a pair in the other sense." loading="lazy" />
</picture>
</figure>
<p>Do you like pair programming? I tried it and I liked it.</p>
<p>Since May, I’ve been working in a team where we regularly do pair programming. This is
my first experience with extensive pairing. We aren’t anywhere close to 100 %,
but even doing a couple of sessions per week is way more pairing than I’ve ever done
before. In general, it has been a positive experience.</p>
<p>Pairing is at its best when both of you unsure about what you’re doing. Having
someone there helps a lot when you’re trying to figure out how something
should work.</p>
<p>My favorite sessions have been the ones where we write the SQL
for the data model of our greenfield application. Even though we’ve sketched
the data model beforehand, there are always some small problems that you have
to think through.</p>
<p>Pairing also works well when one of you is unsure about what you’re doing. That
person should then “drive” (i.e. write the code) while the more knowledgeable
person guides them. This seems like a good way to share knowledge.</p>
<p>Pairing is less useful when both of you know what you’re doing. The person writing
the code does not really need help or guidance. There’s not much to do for the
other person.</p>
<p>Pairing is intensive. I can do it only for two hours or so and then I need to do
something else. I expect this to change with time, but right now I have hard time
imagining pairing full-time.</p>
<p>While I’ve enjoyed pair programming, I find pair debugging frustrating. My working
style when I’m debugging does not suit pair work: I concentrate intensely, jump
quickly around, and rely on my intuition. This is not helpful for either driving
or guiding. My friend <a href="https://twitter.com/freiksenet">Mikhail</a> pointed out that maybe I should
consciously focus on helping others to debug instead of solving the problem. I
have not yet tried this.</p>
<h2 id="pairing-and-code-review">Pairing and code review</h2>
<p>Pairing and code review have similar benefits. They both increase cohesion and
spread knowledge of the code base. They also help at improving the coding skills
and catch bugs and quality issues.</p>
<p>I have a lot of experience with code review and would recommend it for almost
any project except short-lived prototypes. It’s worth it for the knowledge
transfer alone. My updated recommendation is that if you use pairing, you do not
necessarily need code review – you already got most of the benefits with pairing.</p>
<p>My rule of thumb is that all code should be engaged with by (at least) two
persons. To achieve this, we mix these practices: some code is written by
pairing and the rest of it goes through code review.</p>
<p>I’m happy that I have a new, robust tool for producing good code in my toolbox.
If you haven’t yet tried regular pairing, I recommend giving it a go.</p>
Using Beeminder to keep blogging
https://quanttype.net/p/using-beeminder-to-keep-blogging/
Tue, 27 Jun 2017 00:00:00 +0000https://quanttype.net/p/using-beeminder-to-keep-blogging/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/tamperee_hu_d1ee465e467b82ab.webp" type="image/webp" />
<img src="https://quanttype.net/images/tamperee.jpg" width="600" height="450" alt="The nature of Finland inspires me to blog." loading="lazy" />
</picture>
</figure>
<p>I aim to publish a blog post every week, at least when it’s not vacation season.
I’m happy that I’ve published <a href="https://quanttype.net/archive.html">so many blog posts</a> even though
not all of them are very good. Unfortunately actually writing the posts is not that fun.
It’s easy to postpone the task when you’re not feeling inspired.</p>
<p>I’ve solved the problem by using <a href="https://www.beeminder.com">Beeminder</a>. It’s a
service that allows you to make a bet with yourself about reaching some
quantifiable goals. I’ve bet $10 that a new item will appear on the quanttype
RSS feed every week. If it doesn’t happen, I will pay $10 to Beeminder the
company.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> It’s not a big pile of money, but having <em>something</em> on the line
makes the commitment feel more real.</p>
<p>The brilliant part of Beeminder is that you can change the bet any time, but
there’s a delay of one week before the change takes effect. For example, if I
want to give up on my blogging goal right now, that’s perfectly fine, but I
still have to publish one blog post this week. This means that I can’t just
cancel the bet if I’m not feeling inspired some week.</p>
<p>When I tell people about Beeminder, they tell me that it’s weird and
ridiculous. It works, though. I’ve kept blogging and I haven’t lost any money in
a long time.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Sometimes people say that the money should go to a charity or something
instead of Beeminder. If you’re opposed to funding Beeminder, better not to
lose the bet, then. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
What are hybrid maps?
https://quanttype.net/p/clojure-hybrid-maps/
Thu, 15 Jun 2017 00:00:00 +0000https://quanttype.net/p/clojure-hybrid-maps/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/birbs_hu_3dc36b60878e922f.webp" type="image/webp" />
<img src="https://quanttype.net/images/birbs.jpg" width="600" height="478.5" alt="A heterogenous collection of birds." loading="lazy" />
</picture>
</figure>
<p>Clojure people sometimes talk about <em>hybrid maps</em>. What are they talking about?</p>
<p>A map, in general, is a data structure that associates keys with values. There
are two common usage patterns for maps in Clojure:</p>
<ul>
<li>As records. The map acts as a collection of fields with predefined keys. For
example, a row in a relational database is a record. When you query the
database, you know what columns the result will have. Typically in Clojure,
you get a map or a collection of maps as a result of database query.</li>
<li>As indices. The map acts as a collection of objects indexed by something. For
example, you could represent a database table as a Clojure map of rows indexed
by the primary key of each row.</li>
</ul>
<p>See LispCast for <a href="http://www.lispcast.com/clojure-hashmaps">a more extensive discussion of these patterns</a>.</p>
<p>A hybrid map is a map that acts as a record and as an index at the same time.
This pattern is not common – usually it’s better to have the index map as a
field in the record. One case where it works nicely is the Clojure map
destructuring syntax:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">let </span><span class="p">[{</span><span class="nv">var1</span> <span class="ss">:key1</span> <span class="nv">var2</span> <span class="ss">:key2</span> <span class="ss">:keys</span> <span class="p">[</span><span class="nv">key3</span><span class="p">]</span> <span class="ss">:as</span> <span class="nv">result</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">some-function-call</span> <span class="nv">...</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="nv">...</span><span class="p">)</span>
</span></span></code></pre></div><p>Here <code>:keys</code> and <code>:as</code> work like record fields, but the bindings for <code>var1</code> and
<code>var2</code> work like an index.</p>
<h2 id="what-about-heterogenous-maps">What about heterogenous maps?</h2>
<p>A map is <em>heterogenous</em> if it can contain keys and/or values of multiple types.
If a map is not heterogenous, it’s <em>homogenous</em>. Here are some examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:a</span> <span class="mi">1</span> <span class="ss">:b</span> <span class="mi">2</span><span class="p">}</span> <span class="c1">; homogenous</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:a</span> <span class="mi">1</span> <span class="ss">:b</span> <span class="s">"hello"</span><span class="p">}</span> <span class="c1">; heterogenous values</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:a</span> <span class="mi">1</span> <span class="s">"b"</span> <span class="mi">2</span><span class="p">}</span> <span class="c1">; heterogenous keys</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:a</span> <span class="mi">1</span> <span class="s">"b"</span> <span class="s">"hello"</span><span class="p">}</span> <span class="c1">; heterogenous keys and values</span>
</span></span><span class="line"><span class="cl"><span class="p">{}</span> <span class="c1">; could be either!</span>
</span></span></code></pre></div><p>This concept is not discussed much in the Clojure community, because Clojure
maps are heterogenous by default.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> An example of less-heterogenous map in
Clojure is <a href="https://github.com/clojure/data.int-map">data.int-map</a>, where the keys must be integers. The values
can still be anything, though.</p>
<p>The concept of heterogenous maps, and of heterogenous collections in general, is
more interesting in statically typed languages like Scala and Haskell.
Expressing the type of a homogenous map is straightforward, but typing a
heterogenous map is more complex. This is can be seen in how there are separate
libraries for heterogenous collections, like the popular <a href="https://github.com/milessabin/shapeless/wiki/Built-with-shapeless">shapeless</a>
library for Scala.</p>
<p>What’s the relationship between hybrid maps and heterogenous maps? There’s no
relationship, really. Like records, hybrid maps often are heterogenous, but
there’s no reason for why you couldn’t use a homogenous map for a hybrid
purpose.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Unless you argue that Clojure is <a href="https://existentialtype.wordpress.com/2011/03/19/dynamic-languages-are-static-languages/">unityped</a>, in which case all
Clojure maps are homogenous. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
ZuriHac 2017
https://quanttype.net/p/zurihac-2017/
Sun, 11 Jun 2017 00:00:00 +0000https://quanttype.net/p/zurihac-2017/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/zurihac_lake_hu_2a0274a48f26b329.webp" type="image/webp" />
<img src="https://quanttype.net/images/zurihac_lake.jpg" width="600" height="400" loading="lazy" />
<figcaption>The basic dilemma of ZuriHac: to stay inside and code or to enjoy the lake?</figcaption>
</picture>
</figure>
<p><a href="https://zurihac.info">ZuriHac</a>, the Haskell hackathon in/near Zürich, took place this weekend
and it was <a href="https://quanttype.net/posts/2016-07-31-curry-on-zurihac.html">again</a> great!</p>
<p>These days <em>hackathon</em> usually means a prototyping competition. ZuriHac is a
hackathon in a different sense. There’s no competition. Instead, people come
there to collaborate, to learn, and to have fun together. Together we worked on
open-source projects, taught each other Haskell, and had good time drinking beer
by the lake.</p>
<p>There were some talks, too. The keynotes were given by seasoned Haskellers, but
my favorite was the talk given by Mario Meili and Cyrill Schenkel. Mario and
Cyrill are students in <a href="https://www.hsr.ch/Home.home.0.html">HSR Hochschule für Technik Rapperswil</a>, where the
event took place. They presented their work-in-progress master’s theses.</p>
<p>Mario’s part turned out to be a quite provocative conversation opener. His
thesis is about what prevents the wider adoption of Haskell in the industry. He
had gathered often-presented concerns about Haskell like its poor performance.
The listeners were quick to shoot these claims down, but Mario does have a point
– people really do believe these things about Haskell.</p>
<p>My take is that Haskell’s lack of industrial success is not about its drawbacks.
The lack of compelling benefits is the actual problem. It’s hard to make a
concrete case for Haskell.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/zurihac_posse_hu_bde7947da512ed0b.webp" type="image/webp" />
<img src="https://quanttype.net/images/zurihac_posse.jpg" width="600" height="400" loading="lazy" />
<figcaption>Some familiar faces from Helsinki having fun with <a href="https://twanvl.nl/blog/haskell/non-regular1">FunLists</a>.</figcaption>
</picture>
</figure>
<p>While on the plane back home, I
read <a href="http://seankross.com/2017/06/07/A-Year-of-rOpenScis-Unconf.html">this article about the rOpenSci’s Unconference</a>. Sean Kross
describes the Unconf like this:</p>
<blockquote>
<p>Some have described it as a hackathon, but I think that’s a
mischaracterization. Though a great deal of code is written in a short period
of time, a substantial proportion of the attendees make large and important
contributions while hardly writing any code. The Unconf is not a competition,
and it’s intensely social and collaborative.</p>
</blockquote>
<p>Sounds like Unconf’s spirit is similar to ZuriHac’s spirit! This is a great
event format for programming communities – it’s more social and more
collaborative than the usual conferences. After all, everybody seems to agree
that the social part is the best part of conferences.</p>
Commit messages are worthless
https://quanttype.net/p/commit-messages-are-useless/
Fri, 02 Jun 2017 00:00:00 +0000https://quanttype.net/p/commit-messages-are-useless/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kaislat_hu_e29471f891a7c7c4.webp" type="image/webp" />
<img src="https://quanttype.net/images/kaislat.jpg" width="600" height="433" fetchpriority="high" />
<figcaption>Commit messages written in rush tend to be less than helpful.</figcaption>
</picture>
</figure>
<p>A couple of years ago I wrote about how I hoped
that <a href="https://quanttype.net/posts/2013-08-20-why-do-you-write-commit-messages.html">writing good commit messages is worthwhile</a>. It never paid off.</p>
<p>Turns out it’s never <em>my</em> commit messages I’m reading. It’s my coworkers'
messages, or open-source contributors’ messages. It’s not enough that you write
good messages, you need to foster a culture of good commit messages in all the
projects you work with.</p>
<p>Even people who write good commit messages most of the time sometimes create
commits with messages like “make it work” or “WIP” or “blargh”. For some reason
it’s always these commits that contain the mysterious bugs or the unclear code.</p>
<p>Even if you do write good commit messages, if you use GitHub, it’s likely that
nobody reads the messages. GitHub’s user interface isn’t designed for reading
commit messages. When you do a pull request, the commit titles are in small font
and the bodies are hidden. If you use GitHub, it’s better to spend time on
writing good pull request messages. They do get read.</p>
On sailing
https://quanttype.net/p/on-sailing/
Sat, 27 May 2017 00:00:00 +0000https://quanttype.net/p/on-sailing/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/purjevene_hu_ac0a90a909a34f54.webp" type="image/webp" />
<img src="https://quanttype.net/images/purjevene.jpg" width="600" height="438" loading="lazy" />
<figcaption>I was on a boat and it had strings attached.</figcaption>
</picture>
</figure>
<p>I enjoy sailing. I don’t have a sailboat, and I don’t know how to sail, but
sometimes I get lucky and my friends invite me over to their boats.</p>
<p>I like it because when you’re on a boat, you can’t really do anything but sail.
You can’t go anywhere. If there’s good wind, you can go fast and you’re busy
with the sails and navigating. If there’s little wind, you just sit there and
enjoy the sea. It’s one of those in-the-moment activities.</p>
<p>If you haven’t ever sailed and you get the chance, you should take it.</p>
On JSONfeed
https://quanttype.net/p/jsonfeed/
Sat, 20 May 2017 00:00:00 +0000https://quanttype.net/p/jsonfeed/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/heinaa_hu_afa65b4fa4943291.webp" type="image/webp" />
<img src="https://quanttype.net/images/heinaa.jpg" width="600" height="400" loading="lazy" />
<figcaption>I love it when people link to their RSS feed and it’s actually Atom.</figcaption>
</picture>
</figure>
<p>There’s a new alternative to RSS and Atom called <a href="https://jsonfeed.org/">JSON Feed</a>. It’s
like RSS except that it uses JSON instead of XML. Having written a couple of RSS
parsers in my life, I reckon this is a good idea: in many programming languages,
parsing and producing XML is a huge hassle compared to dealing with JSON. If you
try to consume random RSS feeds, you’ll quickly discover how broken they are.</p>
<p>Based on my experience, it’s common to produce XML by concatenating strings or
using a string-based templating language. It’s also common to get this slightly
wrong – the content is poorly escaped or the tags are mismatched.
In contrast, especially in dynamic languages, JSON is easy to produce by
serializing a datastructure. This means that coders in hurry are more likely to
produce syntactically valid JSON than syntactically valid XML.</p>
<p><em>Caveat lector: I do not have experience in consuming random JSON files. My
picture might be way too rosy.</em></p>
<p>The IndieWeb wiki offers
some <a href="https://indieweb.org/feed_file#Criticism">criticism of feed files</a>. I
subscribe to approximately 200 blogs and the point about feeds
becoming out of date or broken is valid. Feeds just mysteriously break even
though the HTML version of the blog continues to work. Sometimes people relocate
their feeds without setting up a redirect. The trouble is that the
publishers never notice because they don’t read their own feeds. It’s up to the
reader to let them know.</p>
<p>A new feed format won’t fix this problem, but then again, I don’t know what
would fix it.</p>
<p>I have yet to create a JSON feed for quanttype, but I might as well do it. If
someone creates a JSON feed module for <a href="https://jaspervdj.be/hakyll/">Hakyll</a>, let me know.</p>
clojure.spec for configuration validation
https://quanttype.net/p/validate-config-with-clojure/
Sun, 14 May 2017 00:00:00 +0000https://quanttype.net/p/validate-config-with-clojure/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kirves_hu_57723905cad52225.webp" type="image/webp" />
<img src="https://quanttype.net/images/kirves.jpg" width="600" height="400" loading="lazy" />
<figcaption>Some tools do not require configuration at all.</figcaption>
</picture>
</figure>
<p>In March, I wrote about <a href="http://www.metosin.fi/blog/configuring-clojure-apps/">configuring Clojure web applications</a>. I
recommended storing the configuration in EDN files and loading them
with <a href="https://github.com/metosin/maailma">Maailma</a>. Something I didn’t mention at all was <em>validating</em> the
configuration.</p>
<h2 id="the-problem">The problem</h2>
<p>What happens if you mistype a configuration key? Clojure is not known for great
error messages and you’ll witness this unless you validate your configuration.</p>
<p>Let’s say you use <a href="http://www.http-kit.org/server.html">HTTP Kit’s HTTP server</a> and your configuration
file looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:http/server</span> <span class="p">{</span><span class="ss">:potr</span> <span class="mi">3000</span><span class="p">}}</span>
</span></span></code></pre></div><p>Maybe you start the server like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">org.httpkit.server</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">run-server</span><span class="p">]])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">start-server</span> <span class="p">[</span><span class="nv">config</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">run-server</span> <span class="nv">app</span> <span class="p">{</span><span class="ss">:port</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">config</span> <span class="p">[</span><span class="ss">:http/server</span> <span class="ss">:port</span><span class="p">])}))</span>
</span></span></code></pre></div><p>But uh oh, there was a typo in the config! It should say <code>:port</code> instead of
<code>:potr</code>. HTTP Kit is going to receive <code>{:port nil}</code>. Can you guess the error
message?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="nv">boot.user=></span> <span class="p">(</span><span class="nf">run-server</span> <span class="nv">app</span> <span class="p">{</span><span class="ss">:port</span> <span class="nv">nil</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="nv">java.lang.NullPointerException</span><span class="err">:</span>
</span></span></code></pre></div><p>Of course. What if you instead pass the whole configuration submap to HTTP Kit?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">start-server</span> <span class="p">[</span><span class="nv">config</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">run-server</span> <span class="nv">app</span> <span class="p">(</span><span class="nb">get </span><span class="nv">config</span> <span class="ss">:http/server</span><span class="p">)))</span>
</span></span></code></pre></div><p>This time you won’t get any error message. The server will quietly ignore the
configuration and start at the default port 8090.</p>
<p>I’m using HTTP Kit as an example, but this problem is not specific to it. It’s
just rare in the Clojure ecosystem to give useful error messages on bad input,
and Clojure’s dynamism does not help here.</p>
<p>This is why configuration validation matters. You’ll save yourself a lot of
debugging time by using a bit of time on validation.</p>
<h2 id="the-clojurespec-solution">The clojure.spec solution</h2>
<p>Clojure 1.9 is going to ship with <a href="https://clojure.org/about/spec">clojure.spec</a>, a library for specifying
and validating data shape. My impression is that it’s primarily intended as a development
and testing tool. I do not have much experience with that yet, but it works
nicely for writing configuration validation code.</p>
<p>Let’s write a spec for the configuration above.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">clojure.spec.alpha</span> <span class="ss">:as</span> <span class="nv">s</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; The top level has one required key, :http/server,</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; specified below</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">::config</span> <span class="p">(</span><span class="nf">s/keys</span> <span class="ss">:req</span> <span class="p">[</span><span class="ss">:http/server</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; :http/server has one required unqualified key, :port,</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; which should be a valid port number.</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">:http/server</span> <span class="p">(</span><span class="nf">s/keys</span> <span class="ss">:req-un</span> <span class="p">[</span><span class="ss">:http/port</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">s/def</span> <span class="ss">:http/port</span> <span class="p">(</span><span class="nf">s/int-in</span> <span class="mi">0</span> <span class="mi">65536</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">validate-config</span> <span class="p">[</span><span class="nv">config</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">when-not </span><span class="p">(</span><span class="nf">s/valid?</span> <span class="ss">::config</span> <span class="nv">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">s/explain</span> <span class="ss">::config</span> <span class="nv">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">throw</span> <span class="p">(</span><span class="nf">ex-info</span> <span class="s">"Invalid configuration."</span> <span class="p">(</span><span class="nf">s/explain-data</span> <span class="ss">::config</span> <span class="nv">config</span><span class="p">))))</span>
</span></span><span class="line"><span class="cl"> <span class="nv">config</span><span class="p">)</span>
</span></span></code></pre></div><p>Let’s try this with our example configuration.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clj" data-lang="clj"><span class="line"><span class="cl"><span class="nv">boot.user=></span> <span class="p">(</span><span class="nf">validate-config</span> <span class="p">{</span><span class="ss">:http/server</span> <span class="p">{</span><span class="ss">:potr</span> <span class="mi">3000</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl"><span class="nv">In</span><span class="err">:</span> <span class="p">[</span><span class="ss">:http/server</span><span class="p">]</span> <span class="nv">val</span><span class="err">:</span> <span class="p">{</span><span class="ss">:potr</span> <span class="mi">3000</span><span class="p">}</span> <span class="nv">fails</span> <span class="nv">spec</span><span class="err">:</span> <span class="ss">:http/server</span>
</span></span><span class="line"><span class="cl"><span class="nv">at</span><span class="err">:</span> <span class="p">[</span><span class="ss">:http/server</span><span class="p">]</span> <span class="nv">predicate</span><span class="err">:</span> <span class="p">(</span><span class="nb">contains? </span><span class="nv">%</span> <span class="ss">:port</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">clojure.lang.ExceptionInfo</span><span class="err">:</span> <span class="nv">Invalid</span> <span class="nv">configuration.</span>
</span></span></code></pre></div><p>Much better, and it took only a couple of lines of code!</p>
What is functional analysis?
https://quanttype.net/p/what-is-functional-analysis/
Sun, 07 May 2017 00:00:00 +0000https://quanttype.net/p/what-is-functional-analysis/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/suokki_hu_a2424b89f74463a2.webp" type="image/webp" />
<img src="https://quanttype.net/images/suokki.jpg" width="600" height="400" loading="lazy" />
<figcaption>Studying functional analysis will help you understand wavelets.</figcaption>
</picture>
</figure>
<p>I took an introductory course on functional analysis. Let me tell you
about it. (See also: <a href="https://quanttype.net/posts/2016-05-24-descriptive-set-theory.html">descriptive set theory</a>, <a href="https://quanttype.net/posts/2016-03-08-what-is-forcing.html">forcing</a>.)</p>
<p>Functional analysis is the study of infinite-dimensional vector spaces. These
spaces are usually (always?) spaces of functions. For example, consider the sequence spaces $\ell^p$ where $1 \leq p <
\infty$. They’re defined as follows:
$$ \ell^p := { (x_n)_{n=1}^\infty : | x |_p := (\sum_{n=1}^\infty | x_n |^p)^\frac{1}{p} < \infty } $$</p>
<p>Here $| x |_p$ is the norm. You can also consider them spaces of functions of
type $\mathbb{N} \rightarrow \mathbb{K}$, where $\mathbb{K}$ is the scalar
field.</p>
<p>All the $\ell^p$ spaces are complete. Thus they’re <em>Banach spaces</em>. Banach
spaces, or complete normed vector spaces, are central to functional analysis
because the functions in them are quite well-behaved. Consider the following
results:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Open_mapping_theorem_(functional_analysis)">Open mapping theorem</a>: A mapping between Banach spaces is open iff it’s
surjective.</li>
<li>If $M$ is a subspace of a Banach space $E$ and $f : M \rightarrow \mathbb{R}$
is a suitable linear function, <a href="https://en.wikipedia.org/wiki/Hahn%E2%80%93Banach_theorem">Hahn-Banach theorem</a> may allow you to
extend it to the whole space $E$.</li>
</ul>
<p>What about the name? Functional analysis is not related to functional
programming. A functional is a function from a vector space to its scalar field.
Of course, in the case of function spaces it is a function of functions.
Functionals often come up in functional analysis – for example the Hanh-Banach
theorem above is about functionals.</p>
<p>Why do people care about functional analysis? A big reason is that it gives
tools for studying partial differential equations. Unfortunately I’m not
familiar enough with the matter to say how it helps, except that having
well-behaved functions is always nice.</p>
JUnit output for Clojure tests
https://quanttype.net/p/junit-output-for-clojure-tests/
Thu, 27 Apr 2017 00:00:00 +0000https://quanttype.net/p/junit-output-for-clojure-tests/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/sorsat_hu_238bb772d55977c8.webp" type="image/webp" />
<img src="https://quanttype.net/images/sorsat.jpg" width="600" height="387.5" loading="lazy" />
<figcaption>I’m trying to get my Clojure testing ducks in a row.</figcaption>
</picture>
</figure>
<p>In January, I wrote about <a href="https://quanttype.net/posts/2017-01-26-clojure-test-runner-of-my-dreams.html">the features I’d like to have in a Clojure test runner</a>. One of them was JUnit XML reports for the test results. I’ve since contributed a JUnit reporter to <a href="https://github.com/weavejester/eftest">Eftest</a>, the test runner library developed by <a href="https://github.com/weavejester">James Reeves</a>.</p>
<p>I had two goals. The first was to support running tests in parallel, which is what Eftest does by default. The second was to produce human-readable output and a JUnit report at the same time. Both of the goals are accomplished by Eftest 0.3.1.</p>
<p>For the second goal, you have to write your own reporting function that calls both the pretty reporter (or the progress reporter, or whatever you like) and the JUnit reporter. Then you can use <code>eftest.report/report-to-file</code> to redirect one of them to a file. Here’s how it goes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">ns </span><span class="nv">my-project.report</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="ss">:require</span> <span class="nv">clojure.test</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">eftest.report</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">report-to-file</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">eftest.report.junit</span> <span class="ss">:as</span> <span class="nv">junit</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">eftest.report.pretty</span> <span class="ss">:as</span> <span class="nv">pretty</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">xml-report</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">report-to-file</span> <span class="p">(</span><span class="nf">junit/report</span> <span class="s">"test-results.xml"</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">report</span> <span class="p">[</span><span class="nv">m</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">pretty/report</span> <span class="nv">m</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">binding </span><span class="p">[</span><span class="nv">clojure.test/*report-counters*</span> <span class="nv">nil</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">xml-report</span> <span class="nv">m</span><span class="p">)))</span>
</span></span></code></pre></div><p>You have to bind <code>*report-counters*</code> to nil when executing one of the reporters to prevent each assertion from getting counted twice. Ideally this would be handled by eftest, but the design of clojure.test makes it hard to do. Check out <a href="https://github.com/weavejester/eftest/issues/23">eftest’s issue 23</a> for discussion.</p>
<p>Now run your tests with <code>{:report my-project.report/report}</code> and you’ll get Eftest’s colorful exception reports in the console and a JUnit report in the file <code>test-results.xml</code>.</p>
<h2 id="test-tagging-and-other-features">Test tagging and other features</h2>
<p>Another feature on my list was filtering tests by tags. Already in January, Eftest had the ability to filter tests with an arbitrary function. Since then, James has created a Leiningen plugin for Eftest and <a href="https://github.com/facundoolano">Facunda Olano</a> has implemented the <a href="https://github.com/technomancy/leiningen/blob/a441ba12ca9723d99e34da2d4aa3483cdf00331b/src/leiningen/test.clj#L191-L199">test selectors</a> supported by <code>lein test</code>.</p>
<p><a href="https://github.com/weavejester/eftest/issues/9">Output catching</a> and the test slowness report are still missing. Output catching is a bit hairy if you want to deal with loggers, but <a href="https://github.com/weavejester/eftest/compare/master...miikka:output-catching">I already wrote a version</a> that catches any output written to <code>*out*</code>. It might be the 80/20 solution. I’ll send a pull request soon. The slowness report should be easy to implement with a reporter function as well.</p>
<p>I think Eftest is going to be the test runner I wished for. I recommend it, and if you want to use it with Boot, <a href="https://github.com/metosin/boot-alt-test">boot-alt-test</a> is a wrapper for it.</p>
<p><em>Update 2017-09-19</em>: Added the binding for <code>*report-counters*</code>. Thanks to Andrew Gnagy for pointing out this problem.</p>
prog1 in Clojure
https://quanttype.net/p/doto-do/
Thu, 20 Apr 2017 00:00:00 +0000https://quanttype.net/p/doto-do/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/tiskit_hu_99ccfde7d533342c.webp" type="image/webp" />
<img src="https://quanttype.net/images/tiskit.jpg" width="600" height="391.5" loading="lazy" />
<figcaption>Clean your code with this one weird macro trick.</figcaption>
</picture>
</figure>
<p>When programming Clojure, sometimes you need the equivalent of Common Lisp’s
<code>prog1</code> macro. It’s like Clojure’s <code>do</code> except that it returns the value of the
<em>first</em> form, not the last one.</p>
<p>You could depend on <a href="https://github.com/amalloy/useful/blob/bcb07414cf3dd5a09794b76490c0cf18758f1888/src/flatland/useful/utils.clj#L26-L32">useful</a>, which calls it <code>returning</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defmacro </span><span class="nv">returning</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Compute a return value, then execute other forms for side effects.
</span></span></span><span class="line"><span class="cl"><span class="s"> Like prog1 in common lisp, or a (do) that returns the first form."</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">value</span> <span class="o">&</span> <span class="nv">forms</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">`</span><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">value#</span> <span class="o">~</span><span class="nv">value</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">~@</span><span class="nv">forms</span>
</span></span><span class="line"><span class="cl"> <span class="nv">value#</span><span class="p">))</span>
</span></span></code></pre></div><p>Of course, when you’re in a hurry, there’s no time for adding new dependencies.
There’s not even time to write your own inline version of the macro. Besides,
they say that you shouldn’t ever write your own macros. So what do you do? You
compose <code>doto</code> and <code>do</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">doto </span><span class="nv">x</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">do</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">z</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; returns x</span>
</span></span></code></pre></div><p><strong>Update (September 2021):</strong> Robert Levy pointed out to me that you can use
<code>constantly</code>. Clever!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">((</span><span class="nb">constantly </span><span class="nv">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">z</span><span class="p">))</span>
</span></span></code></pre></div><p>But maybe instead of <code>x</code> you have <code>(a complicated form)</code> and you want to give
its result a name. Luckily there’s <code>as-></code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">doto </span><span class="p">(</span><span class="nf">a</span> <span class="nv">complicated</span> <span class="nv">form</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">as-></span> <span class="nv">x</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">do</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">y</span> <span class="nv">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">z</span> <span class="nv">x</span><span class="p">))))</span>
</span></span></code></pre></div><p>When you’re debugging a long <code>-></code> thread, a print function that returns the
printed value would be handy so you could insert it in the middle of the chain.
This is of course exactly what <a href="https://github.com/clojure/tools.trace">tools.trace</a> is for. But again, who has
time for adding dependencies? Just use <code>doto</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">-> </span><span class="nv">thing</span>
</span></span><span class="line"><span class="cl"> <span class="nv">do-something-1</span>
</span></span><span class="line"><span class="cl"> <span class="nv">do-something-2</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">doto </span><span class="nv">prn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">do-something-3</span><span class="p">)</span>
</span></span></code></pre></div><p>By the way, the other day I wrote this macro that resembles <code>cond-></code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defmacro </span><span class="nv">if-></span> <span class="p">[</span><span class="nv">expr</span> <span class="nb">cond </span><span class="nv">then</span> <span class="nv">else</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">`</span><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">e#</span> <span class="o">~</span><span class="nv">expr</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">if </span><span class="o">~</span><span class="nb">cond </span><span class="p">(</span><span class="nb">-> </span><span class="nv">e#</span> <span class="o">~</span><span class="nv">then</span><span class="p">)</span> <span class="p">(</span><span class="nb">-> </span><span class="nv">e#</span> <span class="o">~</span><span class="nv">else</span><span class="p">))))</span>
</span></span></code></pre></div><p>I used it with a builder object. The code looked something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">-> </span><span class="p">(</span><span class="nf">Builder.</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.withSetting</span> <span class="s">"password"</span> <span class="s">"kissa2"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">if-></span> <span class="nv">production-mode?</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.useProductionMode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.useTestingMode</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">.build</span><span class="p">))</span>
</span></span></code></pre></div><p>Can somebody figure out how to do that nicely without writing a macro?</p>
Darkroom update
https://quanttype.net/p/darkroom-update/
Fri, 14 Apr 2017 00:00:00 +0000https://quanttype.net/p/darkroom-update/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/tukki_hu_3b7004484f90d4a8.webp" type="image/webp" />
<img src="https://quanttype.net/images/tukki.jpg" width="600" height="432" loading="lazy" />
</picture>
</figure>
<p>It’s Easter holiday and I’m looking forward to photographing
<a href="http://vapaus.org/haronmaki/">Haronmäki</a> again.
This time it’s going to be in black and white,
shot in <a href="https://quanttype.net/posts/2016-05-10-mamiya-rb67.html">medium format</a>.</p>
<p>I’ve been practicing darkroom printing for half a year now and I’m finally
starting to get somewhere. Or at least I’m not making mistakes every time I go
the darkroom. I’m starting to get consistent results and sometimes I even
thinking I’m finding my own “voice”. I had especially good time with
photographing ice this winter.</p>
<p>I’ve wasted a lot of paper, but luckily I’ve ruined only two rolls of film.
Lesson learned: developing two films at once is a great way to ruin two films at
once.</p>
Keskustalaisuus vaalikoneessa
https://quanttype.net/p/keskustalaisuus-vaalikonedatassa/
Sun, 09 Apr 2017 00:00:00 +0000https://quanttype.net/p/keskustalaisuus-vaalikonedatassa/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/icerocks_hu_82d459c0a6a33588.webp" type="image/webp" />
<img src="https://quanttype.net/images/icerocks.jpg" width="600" height="433" loading="lazy" />
</picture>
</figure>
<p><em>Briefly in English: Today is the municipal election day in Finland. As usual, the <em>vaalikone</em> websites are popular. They’re recommendation engines for the elections: you fill in a quiz about your political opinions and the website recommends you candidates whose answers match your answers. Juho Snellman describes them as <a href="https://www.snellman.net/blog/archive/2015-05-11-okcupid-for-voting-the-finnish-election-engines/">OkCupid for elections</a> - I recommend his article for more information. I did some analysis on the vaalikone data from Ilta-Sanomat. Since all the data is in Finnish, the rest of the article is going to be in Finnish as well.</em></p>
<p>Usein väitetään, että keskustalaisuutta ei voi määritellä perinteisellä vasemmisto-oikeisto-akselilla tai oikeisto-liberaali-akselilla. Ainakin Helsingin Sanomien vaalikone tukee tätä ajatusta, sillä <a href="http://www.hs.fi/kaupunki/art-2000005123045.html">Hesarin omassa visualisaatiossa</a> Keskustan ehdokkaat sijoittuvat tasaisesti ympäri arvokarttaa. Mikä siis yhdistää keskustalaisia?</p>
<p>Helsingin Sanomat on julkaissut oman vaalikoneensa ja Ilta-Sanomien vaalikoneen
kysymykset ja vastaukset
<a href="https://github.com/HS-Datadesk/avoindata/tree/master/vaalikoneet/kuntavaalit2017">avoimena datana</a>.
Päätin tutkia, mitkä vaalikonevastaukset ovat erityisen tyypillisiä
keskustalaisille. Tarkastelin
<a href="http://www.vaalikone.fi/kunta2017/is/">Ilta-Sanomien vaalikoneen</a> kysymyksiä,
sillä ne suorasanaisempia kuin HS:n vastaavat. Alempana kerron siitä,
miten analysoin dataa, mutta otetaan hauskin ensin eli tulokset.</p>
<hr>
<p>Seuraavaan
kahteen väitteeseen samaistuminen lisää kaikkein eniten todennäköisyyttä olla
keskustan kunnallisvaaliehdokas:</p>
<ul>
<li>Samaa mieltä: Koko Suomi on syytä pitää asuttuna, vaikka se tietäisikin veronmaksajille kustannuksia.</li>
<li>Eri mieltä: Hyvä veli -verkostot ohjaavat kuntien päätöksentekoa.</li>
</ul>
<p>Alla ovat tulokset muillekin suurpuolueille aakkosjärjestyksessä:</p>
<p><strong>Kokoomus</strong></p>
<ul>
<li>Samaa mieltä: Julkisia palveluita tulisi ulkoistaa entistä enemmän yksityisten yritysten tuotettavaksi.</li>
<li>Eri mieltä: On parempi, että kunta nostaa veroäyriä kuin että se leikkaa palveluistaan.</li>
</ul>
<p><strong>Kristillisdemokraatit</strong></p>
<ul>
<li>Eri mieltä: Homo- ja lesbopareilla pitää olla samat avioliitto- ja adoptio-oikeudet kuin heteropareilla.</li>
<li>Eri mieltä: Eutanasia pitäisi sallia.</li>
</ul>
<p><strong>Perussuomalaiset</strong></p>
<ul>
<li>Eri mieltä: Kotikuntani pitäisi ottaa aktiivisesti vastaan kotoutettavia turvapaikanhakijoita.</li>
<li>Eri mieltä: Monikulttuurisuus on kunnalle hyvä asia.</li>
</ul>
<p><strong>RKP</strong></p>
<ul>
<li>Eri mieltä: Tuloerojen kasvusta on haittaa yhteiskunnalle.</li>
<li>Samaa mieltä: Kotikuntani pitäisi ottaa aktiivisesti vastaan kotoutettavia turvapaikanhakijoita.</li>
</ul>
<p><strong>SDP</strong></p>
<ul>
<li>Eri mieltä: Julkisia palveluita tulisi ulkoistaa entistä enemmän yksityisten yritysten tuotettavaksi.</li>
<li>Eri mieltä: Hyvä veli -verkostot ohjaavat kuntien päätöksentekoa.</li>
</ul>
<p><strong>Vasemmistoliitto</strong></p>
<ul>
<li>Eri mieltä: Julkisia palveluita tulisi ulkoistaa entistä enemmän yksityisten yritysten tuotettavaksi.</li>
<li>Eri mieltä: Kerjääminen kaduilla pitäisi kieltää lailla.</li>
</ul>
<p><strong>Vihreät</strong></p>
<ul>
<li>Samaa mieltä: Kasvisruoan määrää pitäisi lisätä kouluissa ja liharuuan vähentää.</li>
<li>Samaa mieltä: Homo- ja lesbopareilla pitää olla samat avioliitto- ja adoptio-oikeudet kuin heteropareilla.</li>
</ul>
<hr>
<p>Ainakaan minulle näissä ei ollut suuria yllätyksiä. Keskustalaisilla korostuu
aluepolitiikka. En tiedä mitä odotin. Hyvä veli -verkostot hieman naurattivat,
mutta lienee luonnollista, että kuntavaltaa käyttävien puolueiden ehdokkaat ovat
sitä mieltä, että vallankäyttö toimii. Myös Kokoomus sai korkean positiivisen kertoimen kyseiselle väitteelle.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/nuoli_hu_9953df058190cba7.webp" type="image/webp" />
<img src="https://quanttype.net/images/nuoli.jpg" width="600" height="439" loading="lazy" />
</picture>
</figure>
<h2 id="teknisiä-yksityiskohtia">Teknisiä yksityiskohtia</h2>
<p>Minulla ei ole tilastotieteellistä koulutusta, joten en uskalla luvata paljoakaan tämän analyysin luotettavuudesta. Luultavasti se ei ole kovin luotettava.</p>
<p>Mallinsin tilannetta logistisella regressiolla. Selittävinä muuttujina ovat vastaukset kysymyksiin jatkuvalla asteikolla 1-5 (missä 1 tarkoittaa ”täysin eri mieltä” ja 5 ”täysin samaa mieltä”) ja selitettävänä muuttujana on kulloinkin analysoitavan puolueen jäsenyys. Ylle valitsin ne kaksi kysymystä, joita vastaavat kertoimet olivat suurimmat. Kertoimet voidaan tulkita <a href="https://en.wikipedia.org/wiki/Odds_ratio">vetosuhteen</a> logaritmeina. Jätin kuitenkin vetosuhteet esittämättä, sillä en osaa arvioida niiden luotettavuutta.</p>
<p>HS:n vaalikonedatassa on samassa tiedostossa sekä Ilta-Sanomien että HS:n
vaalikoneen kysymykset. Käytin pelkästään IS:n kysymyksiä. Toisin kuin HS:n
vaalikoneessa, IS:n vaalikoneessa ei ole kuntakohtaisia kysymyksiä.</p>
<p><em><a href="https://github.com/HS-Datadesk/avoindata/tree/master/vaalikoneet/kuntavaalit2017">HS:n ja IS:n kuntavaalikoneen data</a> on julkaistu CC BY 4.0 -lisenssillä.</em></p>
How I use Anki
https://quanttype.net/p/how-i-use-anki/
Sun, 02 Apr 2017 00:00:00 +0000https://quanttype.net/p/how-i-use-anki/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/uurteet_hu_f1cda8957ffc9e48.webp" type="image/webp" />
<img src="https://quanttype.net/images/uurteet.jpg" width="600" height="430.5" alt="Etch math in your memory like the ice etched this rock." loading="lazy" />
</picture>
</figure>
<p>I’ve been using <a href="http://ankisrs.net">Anki</a> for a couple of years and it’s great. It’s a flashcard software - basically a tool for memorizing things. If you’re not familiar with it, Gwern has <a href="https://www.gwern.net/Spaced%20repetition">an introduction</a> and Robb Seaton has <a href="http://rs.io/anki-tips/">some helpful tips</a>.</p>
<p>For a long time, I thought Anki was cool, but I didn’t really know how to use it. I wasn’t actively studying any foreign languages or anything like that. The topics (like mathematics) I was learning about didn’t seem to be that amenable for memorizing. It took me a while, but I’ve now figured out a way that works for me, and it applies to almost any topic.</p>
<p><strong>Whenever I need to look something up twice, I add it to Anki.</strong> For example, English is not my native language, so I often look up definitions of English words in a dictionary. If I look up a word twice in a short period of time, I add it and its Finnish definition to Anki. If a word strikes me as especially important, I might add it already on the first lookup. Now my deck has cards for words like <em>flaccid</em>, <em>amygdala</em>, and <em>pithy</em> (that’s a hard one, I always mix it up with <em>pitiful</em>).</p>
<p>I’ve had success with memorizing mathematical identities as well. After years of struggling to remember the formula for the limit of a geometric series, I finally added it to Anki and now I remember it. I have some more abstract mathematical cards, too. I had to think it through a couple of times recently, so now added a card ”What’s the relationship between Borel sets and the Lebesgue measure?” to my deck (answer: ”Borel sets are Lebesgue measurable but not vice versa”).</p>
<p>People sometimes oppose memorizing because you can just look things up when needed, especially now that we have Internet, or derive them when it comes to mathematics. You can’t <em>always</em> look up or derive <em>everything</em>, though. First, it takes too much time, and second, it’s easier to look things up when you already know something related to them.</p>
<p>I figure that if I need to look up something repeatedly, I might as well add it to the base of condensed knowledge in my head.</p>
Configuring Clojure apps
https://quanttype.net/p/configuring-clojure-apps/
Thu, 23 Mar 2017 00:00:00 +0000https://quanttype.net/p/configuring-clojure-apps/<p>For the record, I’ve published a post about
<a href="http://www.metosin.fi/blog/configuring-clojure-apps/">configuring Clojure web applications</a> on Metosin’s blog. Something I did
not write about is how to configure the ClojureScript frontend. If you have
any insights into this, please blog about it!</p>
Thinking about gear (acquisition syndrome)
https://quanttype.net/p/against-gear-acquisition/
Sun, 19 Mar 2017 00:00:00 +0000https://quanttype.net/p/against-gear-acquisition/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/reel_hu_4860cc380a703878.webp" type="image/webp" />
<img src="https://quanttype.net/images/reel.jpg" width="600" height="400" alt="Parts of a film development tank. I ruined the film in the picture. :(" loading="lazy" />
</picture>
</figure>
<p>One of the distractions of the hobbyist photographer is the Gear Acquistion
Syndrome (GAS): instead of making photographs, you spend all your time obsessing
over new gear. Your current gear seems inadequate when there are so much better
options available.</p>
<p>This is not exclusive to photographers: I’ve seen at least
guitarists and cyclists do this, too. Basically there are two distinct versions of these hobbies: the main hobby and
the gear hobby.
There’s nothing wrong with this. When it comes to your free
time, you do you.</p>
<p>Why does Gear Acquisition Syndrome cause distress, then?
It’s because your priorities and actions are in conflict: you’re focusing on the
gear hobby when you feel you should be focusing the main hobby.</p>
<p>I’ve had my bouts of photography GAS, but I nowadays get easily over it. Let me
share some of the thoughts that helped me.</p>
<p>First, clarify your priorities. Instead of thinking of what you <em>should</em> do,
think about <a href="http://kajsotala.fi/2015/10/changing-language-to-change-thoughts/">what you <em>want</em> to do</a>. Should is sneaky,
<a href="http://blog.aurynn.com/the-endless-war-of-should">beware should</a>! Myself, I care about making greats photographs much
more than I care about having a great camera. Some people may find that they in
fact
prefer cameras to photographs. This is fine, too.</p>
<p>Once you understand your priorities, think about what’s the bottleneck in your
process. Have you reached the limits of your gear? For example, I could buy a
new digital camera with a bigger sensor. The sensor size is clearly something
that people care about, as evidenced by how enthusiastic people are about
<a href="https://petapixel.com/2017/01/20/fujifilm-medium-format-gfx-50s-first-impressions/">the new era of medium format digital cameras</a>. Despite this, I’m unable to
argue how this would make my photos better. The sensor size is not my
bottleneck – the time spent on photography is.</p>
<p>Finally, it’s helpful to study the old masters. It’s easy to forget how
amazingly good and easy to use everything on the market right now is. Compared
to the modern equipment, the photographers of the early and even the late 20th
century used convoluded crap. Nevertheless they managed to make pictures that
speak to me decades later. Your current gear likely is good enough for pretty
great photography.</p>
<p>Of course, it’s easy for me to say this. My photography is decidedly
non-technical. So maybe you <em>do</em> need that new lens after all…</p>
Ice in black and white
https://quanttype.net/p/ice-in-black-and-white/
Sun, 12 Mar 2017 00:00:00 +0000https://quanttype.net/p/ice-in-black-and-white/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/poiju_hu_7906fd405c1887f.webp" type="image/webp" />
<img src="https://quanttype.net/images/poiju.jpg" width="600" height="433.5" alt="A buoy surrounded by ice." loading="lazy" />
</picture>
</figure>
<p>I wrote a blog post about Clojure, but I realized it was a cheap rant and
deleted it. Nevertheless I must fill this week’s blogging quota. Please look at
this photo instead. I love the look of ice in black and white.</p>
The surprises of photography
https://quanttype.net/p/surprises-of-photography/
Sat, 04 Mar 2017 00:00:00 +0000https://quanttype.net/p/surprises-of-photography/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kallahti_hu_84350dd734b65094.webp" type="image/webp" />
<img src="https://quanttype.net/images/kallahti.jpg" width="600" height="485.5" loading="lazy" />
</picture>
</figure>
<p>Roland Barthes’s <em>Camera Lucida</em> is a book about what the essence of photography is. I’ve been studying it with the <a href="http://www.ylioppilaskamerat.fi/in-english/">university photography club</a>’s reading group. What it says about surprise got my attention.</p>
<p>In Chapter 14, Barthes writes about the role of surprise in photography:</p>
<blockquote>
<p>I imagine (this is all I can do, since I am not a photographer) that the essential gesture of the <em>Operator</em> is to surprise something or someone (through the little hole of the camera), and that this gesture is therefore perfect when it is performed unbeknownst to the subject being photographed.</p>
</blockquote>
<p>Barthes goes on to list five different modes of surprise.</p>
<ol>
<li><em>The rare</em>, where the subject is something uncommon like a person with two heads.</li>
<li><em>The decisive moment</em>, where rapid action is frozen during its course. For example a person jumping out of window.</li>
<li><em>The prowess</em>, where the photograph displays extreme technical skill. For example a photograph of the explosion of a drop of milk.</li>
<li><em>Contortions of technique</em>, where the photographer deliberately plays tricks with the medium. For example a photograph created by superimposition.</li>
<li><em>La trouvaille</em>, which is the lucky finding of the perfectly composed natural scene.</li>
</ol>
<p>Barthes mainly cares about photographs of people. I’m focused on landscape photography, though, what about that? How are you going to surprise a landscape? It does make sense: an essential part of landscape photography is finding the perfect light. You’re trying to capture the decisive moment of the sun.</p>
<p>The book was published in 1980. The contortions of technique may have lost some of their power since then. The digital tools have made it so easy to create impossible images that they do not have the same surprise value anymore. In general, Barthes argues that the power of photography comes from its ability to depict what has been. I wonder what Barthes would think now that the testimonial value of a photograph has great declined.</p>
<p>Barthes concludes the chapter:</p>
<blockquote>
<p>In an initial period, Photography, in order to surprise, photographs the notable; but soon, by a familiar reversal, it decrees notable whatever it photographs. The ”anyhing whatever” then becomes the sophisticated acme of value.</p>
</blockquote>
<p>This is something for us amateur photographers to keep in mind. A photograph is not interesting just because it exists.</p>
Reading highlights from 2016
https://quanttype.net/p/reading-highlights-2016/
Sat, 25 Feb 2017 00:00:00 +0000https://quanttype.net/p/reading-highlights-2016/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kirjat_hu_ee8d0c0e742c427d.webp" type="image/webp" />
<img src="https://quanttype.net/images/kirjat.jpg" width="600" height="400" loading="lazy" />
<figcaption>The stack of books mentioned in this post.</figcaption>
</picture>
</figure>
<p>I want to highlight some of the best books I read in 2016. None of them were
actually published in 2016 and they’re all well-known, popular books. Looks like
my taste in books agrees with the masses!</p>
<h2 id="non-fiction">Non-fiction</h2>
<p><strong>Dreams from My Father</strong> by Barack Obama. This is a memoir by Obama first published in 1995, way before he became a president. He writes about his childhood in Hawaii and Indonesia, about his college studies, about how he worked as a community organizer in Chicago, and about how he visited his relatives in Kenya. The book conveys a sense of rootlessness: Obama isn’t quite Hawaiian, but he’s no Kenyan either. He also touches on the troubles of being black in the US, with themes similar to Ta-Nehisi Coates’s book <em>Between the World and Me</em> (highly recommended!). I recommend this book to everyone. (<a href="https://smile.amazon.com/gp/product/1400082773">Amazon</a>, <a href="https://www.goodreads.com/book/show/6163289-dreams-from-my-father">Goodreads</a>)</p>
<p><strong>Crossing the Chasm</strong> by Geoffrey A. Moore. This is a classic marketing book about the struggles of high-tech companies when they try to mainstream their innovative products. The book observes that what is easy to sell to the early adopters, who look for game-changing innovation, does not work in the mainstream market, where fully-developed products are needed. The book then suggests that the way forward is to focus on taking over a suitable niche of the mainstream market. The book has been around for a while, so I assume that actual marketing professionals are well aware of its ideas. For me, as a tech professional working with high-tech companies, it was helpful for understanding the business environment where the companies operate. I recommend this book to tech people. (<a href="https://smile.amazon.com/gp/product/B000FC119W/">Amazon</a>, <a href="https://www.goodreads.com/book/show/5732745-crossing-the-chasm">Goodreads</a>)</p>
<h2 id="fiction">Fiction</h2>
<p><strong>Not Before Sundown</strong> by Johanna Sinisalo. It’s a story about a photographer called Angel, who finds a wounded troll and starts taking care of it. Soon the troll becomes entangled with Angel’s romantic and professional life. A short, unique book mixing urban life and Finnish mythology! I read it in Finnish, but I hear the English translation is good, too. I recommend this book to everyone. (<a href="https://smile.amazon.com/gp/product/B008UX3PAG">Amazon</a> with its other English title, <em>Troll: A Love Story</em>, <a href="https://www.goodreads.com/book/show/15774310-ennen-p-iv-nlaskua-ei-voi">Goodreads</a>)</p>
<p><strong>The Three-Body Problem</strong> by Liu Cixin. This is a bestseller, so if you read sci-fi, you’ve probably heard about it. It’s a story about an encounter with alien species, spanning from the Cultural Revolution to the present day. The characters are flat – they seem to all be nihilists – but the sci-fi ideas are a lot of fun. Unfortunately I don’t know what to say about them without spoiling the book. I recommend this book to people who like hard sci-fi. (<a href="https://smile.amazon.com/gp/product/B00S8FCJCQ/">Amazon</a>,<a href="https://www.goodreads.com/book/show/24856109-the-three-body-problem">Goodreads</a>)</p>
Technical tooling for making better software
https://quanttype.net/p/technical-tools-for-better-software/
Thu, 16 Feb 2017 00:00:00 +0000https://quanttype.net/p/technical-tools-for-better-software/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/jacket_hu_df9b7918be7b9535.webp" type="image/webp" />
<img src="https://quanttype.net/images/jacket.jpg" width="600" height="400" loading="lazy" />
<figcaption>Ripping seams is easy when you use the right tool.</figcaption>
</picture>
</figure>
<p>David R. MacIver has a list of <a href="http://www.drmaciver.com/2016/10/some-things-that-might-help-you-write-better-software/">some things that might help you make better software</a>. Some of the things, like continuous integration, require technical tooling in addition to adopting the practices.</p>
<p>If you agree with David’s list – and I do - this raises a question: does your technical stack provide the tooling needed for implementing David’s suggestions? In my case, do these tools exist for Clojure and ClojureScript? Let’s find out.</p>
<p><strong>Continuous integration.</strong> No problems here: CI servers are mostly language-agnostic. If you want a hosted solution, there’s e.g. <a href="https://travis-ci.org">Travis</a> and <a href="https://circleci.com">Circle</a>, and if you prefer running your own, <a href="https://jenkins.io">Jenkins</a> is always there.</p>
<p><strong>Local automated tests.</strong> For Clojure, things are okay. <a href="https://quanttype.net/posts/2017-01-26-clojure-test-runner-of-my-dreams.html">Test runners are not perfect</a>, but there’s a reasonable workflow for running single tests in common editors like Emacs and Cursive, and running the whole test suite obviously works. For ClojureScript, <a href="https://github.com/miikka/karma-demo">there’s a workable setup for the whole test suite</a>. In theory, the same editor workflow should work for ClojureScript, but in practice I’ve always encountered problems with the ClojureScript REPL.</p>
<p><strong>Code coverage.</strong> For Clojure, there’s <a href="https://github.com/cloverage/cloverage">cloverage</a>, although I’ve had some trouble with making it work with all the projects. As far as I know, there’s nothing for ClojureScript yet. Maybe something could be whipped up with <a href="https://github.com/gotwarlost/istanbul">Istanbul</a>and <a href="https://github.com/SitePen/remap-istanbul">remap-istanbul</a>?</p>
<p><strong>Property-based testing.</strong> There’s <a href="https://github.com/clojure/test.check">test.check</a>, which David ranks as <a href="http://hypothesis.works/articles/quickcheck-in-every-language/">very good</a>. clojure.spec’s <a href="https://clojure.org/guides/spec#_generators">generator support</a> makes it even nicer. It works with both Clojure and ClojureScript.</p>
<p><strong>Static analysis.</strong> Cursive does some in-editor analysis with both Clojure and ClojureScript. For Clojure, there’s <a href="https://github.com/jonase/eastwood">Eastwood</a>, which does not work with every project, but it is nice when it works. There’s also <a href="https://github.com/jonase/kibit">kibit</a>, which is less useful, but also works with ClojureScript.</p>
<p><strong>Production error monitoring.</strong> I’ve actually never done this with Clojure or ClojureScript, but I know that there’s at least <a href="https://sentry.io">Sentry</a> tooling for various Java and Clojure logging libraries. Sentry also supports JavaScript error tracking, which might be good enough for ClojureScript.</p>
<p><strong>Auto formatting and style checking.</strong> At least both Cursive and Emacs have support for reformatting Clojure(Script) code. <a href="https://github.com/weavejester/cljfmt">cljfmt</a> can both check and format your code and <a href="https://github.com/dakrone/lein-bikeshed">lein-bikeshed</a> checks some things as well. They’re not quite as advanced as something like <a href="http://eslint.org/docs/rules/">ESLint</a>, though.</p>
<p>Overall, I’d rate the situation as okay but not amazing. If you look at David’s list, having the right processes and the right culture will have much bigger impact than having good tools. On the other hand, changing any processes involving tools is easier when the tools actually work.</p>
Running ClojureScript tests with Karma
https://quanttype.net/p/clojurescript-karma/
Wed, 08 Feb 2017 00:00:00 +0000https://quanttype.net/p/clojurescript-karma/<p><a href="https://quanttype.net/posts/2017-01-26-clojure-test-runner-of-my-dreams.html">In the previous post</a>
I mentioned that you can use <a href="http://karma-runner.github.io/">Karma</a> to run
ClojureScript tests. The easiest way to do it is to use
<a href="https://github.com/bensu/doo">doo</a> or
<a href="https://github.com/crisptrutski/boot-cljs-test">boot-cljs-test</a>. Sometimes you
need more advanced configuration and it’s best to use Karma directly. I’ve set
up <a href="https://github.com/miikka/karma-demo">a GitHub repository</a> that shows how to
do it.</p>
<p>It’s actually pretty straightforward, but that was not obvious to me before I
tried to do it.</p>
Clojure test runner of my dreams
https://quanttype.net/p/clojure-test-runner-of-my-dreams/
Thu, 26 Jan 2017 00:00:00 +0000https://quanttype.net/p/clojure-test-runner-of-my-dreams/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kallio4.opt_hu_a718d283a1ccfd26.webp" type="image/webp" />
<img src="https://quanttype.net/images/kallio4.opt.jpg" width="600" height="400" loading="lazy" />
</picture>
</figure>
<p>How do you run your Clojure unit tests? Does it make you happy?</p>
<p>When working on new code, I rely on <a href="https://cider.readthedocs.io/en/latest/running_tests/">CIDER’s clojure.test support</a>. It
allows you to run all tests in a namespace and then re-run the failed ones after
you’ve made changes. This is good, because it makes the feedback loop tight:
write code, send it to the REPL, run the tests, repeat.
<a href="https://cursive-ide.com/userguide/testing.html">Cursive supports a similar workflow</a>.</p>
<p>When I want to run the full test suite from the command-line, for example in a
CI job, none of the available test runners makes me fully happy. For large test
suites - especially the ones with integration tests - I’d like to have the
following:</p>
<ul>
<li><strong>Output catching.</strong> If a test prints something to stdout, it should be shown
only if the test fails. There’s no point in wading through thousands of lines
of logs generated by succesful tests to find that one actual exception.</li>
<li><strong>Test tagging.</strong> Many test runners allow you to filter tests by name. I’d
like to filter them by custom tags. For example, I’d like to say “run
all the tests except those tagged with <code>:large</code>”.</li>
</ul>
<p>Here are some less essential but still nice to have features:</p>
<ul>
<li><strong>JUnit output in addition to the normal output.</strong> CI tools like Jenkins or
Circle know how to create nice reports from JUnit output. The reports are
helpful for making sense of large test suites. <a href="https://clojure.github.io/clojure/clojure.test-api.html#clojure.test.junit/with-junit-output">clojure.test</a> knows
how to generate JUnit, but it can only do it <em>instead</em> of the normal output.
When debugging test failures, I prefer the usual logs to the nice reports.</li>
<li><strong>Reporting the slowest tests.</strong> This is not something I care about all the
time, but it’s handy when you wonder why the full test suite takes 45 minutes
to run.</li>
</ul>
<p>I’ve seen or implemented all of the above in custom test runners, but none of
the open-source runners – <code>lein test</code>, <a href="https://github.com/adzerk-oss/boot-test">boot-test</a>,
<a href="https://github.com/weavejester/eftest">eftest</a>, <a href="https://github.com/metosin/boot-alt-test">boot-alt-test</a>, … – offers everything in a
coherent package. Basically what I want is <a href="http://doc.pytest.org/en/latest/index.html">pytest</a> for Clojure.
Supporting ClojureScript would be great, too, although you can already use
<a href="https://karma-runner.github.io/1.0/index.html">Karma</a> with <a href="https://github.com/honzabrecka/karma-cljs-test">karma-cljs-test</a>.</p>
<p>Since I know what I want, I should just go ahead and implement this, right?
Right. I wanted to try something new and first write about this to see if anyone
else cares. Does anyone else miss these features?</p>
<p><em>Update 2017-04-27</em>: <a href="https://quanttype.net/posts/2017-04-27-junit-output-for-clojure-tests.html">Check out the update on progress!</a></p>
Curry On and ZuriHac
https://quanttype.net/p/curry-on-zurihac/
Sun, 31 Jul 2016 00:00:00 +0000https://quanttype.net/p/curry-on-zurihac/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/rome_hu_e4a5a9400c86cecc.webp" type="image/webp" />
<img src="https://quanttype.net/images/rome.jpg" width="600" height="450" loading="lazy" />
<figcaption>The venue for Curry On after party was impressive.</figcaption>
</picture>
</figure>
<p>Last week I attended <a href="http://curry-on.org/2016/">Curry On</a> in Rome and <a href="https://wiki.haskell.org/ZuriHac2016">ZuriHac</a> in Zürich.</p>
<h2 id="curry-on">Curry On</h2>
<p>The idea of Curry On is to bring together academic and industry people to talk about programming languages. There were talks about programming languages, tools for making programming languages, tools for analysing programs etc. I’d like summarize it as <em>language-driven development</em>: you create a custom language in which it’s easy to express and solve your problems.</p>
<p>In general, the talks were of high quality. I was positively surprised by the quality of the Q&A sessions as well - especially the senior academics made insightful questions and remarks. The talk videos are <a href="https://www.youtube.com/c/curryon">available on YouTube</a> if you want to watch them. Here are my favorites:</p>
<p><a href="https://www.youtube.com/watch?v=-cLI3GHvLOM"><strong>The Racket Manifesto</strong></a> by Matthias Felleisen was about how Racket is programming language for creating languages and what this means, what problems there are and how Racket solves them. I was very impressed. This talk makes a nice pair with Larry Wall’s <a href="https://www.youtube.com/watch?v=BJIfPFpaMRI">Perl 6 keynote</a> - Perl 6 has a similar goal, but it’s philosophy is quite different.</p>
<p><a href="https://www.youtube.com/watch?v=20WVE3bkYrQ"><strong>Why the Free Monad isn’t Free</strong></a> by Kelley Robinson. Robinson first gave an explanation of what free monads are in the context of Scala and then discussed the cognitive costs associated with such advanced abstractions. The learning curve can be steep and it can make a piece of software unmaintainable. It’s a fair point and I haven’t seen much discussion about it.</p>
<p>The conference did reinforce my belief that programming languages matter. In
this age of JavaScript <a href="https://quanttype.net/posts/2016-06-07-you-should-look-beyond-js.html">I’ve had my doubts</a>.</p>
<h2 id="zurihac">ZuriHac</h2>
<p>ZuriHac is a Haskell hackathon. It’s one of those old-school hackathons where
instead of competing, you gather together to hack on open source projects. There
aren’t that many Haskell conferences, so ZuriHac has become an important meeting
point for the European Haskell community. I liked this: hackathons feel more
social than conferences and there are less talks to be ignored. Basically
hackathons are like hallway-track-only conferences.</p>
<p>I did not get that much done, but at least I got in
<a href="https://github.com/agda/agda/pull/2110">a small patch</a> to Agda. Hopefully it
enables some further work on the Agda JS backend, which isn’t in very good state
right now. Zürich is a beautiful city and I had great time - I hope to be there
next year as well.</p>
Some questions (June 2016)
https://quanttype.net/p/some-questions/
Tue, 28 Jun 2016 00:00:00 +0000https://quanttype.net/p/some-questions/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/lettu_hu_90dd4c28723c2f16.webp" type="image/webp" />
<img src="https://quanttype.net/images/lettu.jpg" width="600" height="448.2" loading="lazy" />
<figcaption>How does one make the perfect pancake?</figcaption>
</picture>
</figure>
<p>Once before I wrote about
<a href="https://quanttype.net/posts/2015-09-06-some-questions-i-cant-answer.html">some questions I couldn’t answer back then</a>. Instead of now
answering them, I’m going to ask some new questions. I’ve been thinking about
these lately.</p>
<p><strong>What are coder super powers?</strong> What are some examples of programmer skills
that are highly valuable and very rare at the same time? Can I learn some of
them?</p>
<p><strong>When is it worthwhile to update your application’s dependencies?</strong> If you
upgrade early, you get all the bugs and the compatibility problems. If you
upgrade late, you’ll miss out on the bug fixes and the new features. I’m eager
to update dependencies, but I’m not sure I can argue that it’s time well spent.</p>
<p><strong>Is generative testing worthwhile?</strong> I’ve tried to use it several times and
I’ve only ever found trivial bugs. Experts would argue that I’m doing it wrong.
Even if not, the trivial bugs accumulate. A similar argument applies to the
correctness-enforcing abilities of static typing and code review.</p>
<p><strong>Is there a pattern where smart developers create software with bad
architecture?</strong> If you’re smart enough, you can make software work even if it
has catastrophic architecture. If you were less smart, you couldn’t deal with
the complexity and would have to come up with a design that actually fits the
problem.</p>
On feeling guilty about not being good enough
https://quanttype.net/p/on-feeling-guilty-about-not-being-good-enough/
Tue, 21 Jun 2016 00:00:00 +0000https://quanttype.net/p/on-feeling-guilty-about-not-being-good-enough/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/portti_hu_294a9edec1de6cd4.webp" type="image/webp" />
<img src="https://quanttype.net/images/portti.jpg" width="600" height="400.2" loading="lazy" />
</picture>
</figure>
<p><em>Summary: Guilt is a poor source of motivation.</em></p>
<p>On a certain IRC channel, we’ve had a series of discussions about how a dabbling
software developer can become an established professional. One of the recurring
topics is feeling guilty for not being good enough.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>There are a lot of highly visible cool programmers with out there. Their
knowledge is deep and wide, they’re pushing out popular open-source libraries,
they’re giving spectacular talks, they’re working for or starting prestigious
companies.</p>
<p>As a junior developer, it’s easy to conclude that this is what you <em>need</em> to do
to be a professional programmer. A junior developer seldom does any of these.
This can make you feel guilty.</p>
<p>Of course, nobody can realistically expect a junior to do those things. Not
every experienced developer does those things, or wants to. It takes time and
effort to have impact and to become well-known, even if you’re lucky and
privileged. You eat an elephant one bite at a time, as they say.</p>
<p>The guilt may still be there, though, even when you know all this.</p>
<p>I don’t know how to get rid of it, but I want to tell you my story with guilt.
It’s about math instead of code, but the logic is the same.</p>
<p>As a kid, I was always considered ”talented” in mathematics. I did very well in
math in the high school and eventually ended up studying it in the university.
The high school math needed no effort, but the university math was harder. Turns
out you have to work for your grades in the university!</p>
<p>Some students seemed to solve the exercises and pass the exams without any
effort<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I felt like I should be able to do it easily as these other students
did but I couldn’t. This made me feel bad about the exercises, which made me
avoid them, which prevented me from learning to solve them, which made feel even
worse about them. The vicious circle was complete. I pretty much dropped out of
the university because of this.</p>
<p>I don’t know what changed it, but eventually I stopped caring about how easy the
exercises <em>should</em> be. Instead I started to focus on how much I liked learning
math. Studying became something I looked forward to. It has gone to the point
that I’m excited about an upcoming exam, because it’s a reason to study math.</p>
<p>It’s a long road to become great at what you do, whether it’s software
development, mathematics, or something completely different. I hope you can find
a way to enjoy your journey.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>There’s probably a catchy name for this.
<a href="https://en.wikipedia.org/wiki/Impostor_syndrome">Impostor syndrome</a> is
similar but not the same. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>It was not true. I’ve later learned that they did in fact put in a lot of
effort, just like everyone else who succeeds in mathematics. I just failed
to notice it back then. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
You'll want locally scoped CSS
https://quanttype.net/p/youll-want-local-scope-in-css/
Tue, 14 Jun 2016 00:00:00 +0000https://quanttype.net/p/youll-want-local-scope-in-css/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/portaat_hu_88e3c050065169b3.webp" type="image/webp" />
<img src="https://quanttype.net/images/portaat.jpg" width="600" height="337.2" loading="lazy" />
<figcaption>Cascading is like class inheritance: mostly to be avoided.</figcaption>
</picture>
</figure>
<p><em>Summary: Avoid cascading in CSS; use <a href="http://getbem.com/">BEM</a>, <a href="https://github.com/css-modules/css-modules">CSS modules</a>, ar other
similar tools.</em></p>
<p>At <a href="http://www.metosin.fi/">work</a>, we recently took over a complex web
application that has been developed over many years. I’m working on improving
the front-end. I like the architecture, the code is straightforward and there
are tests. Based on the code bases I’ve seen, the situation looks good. There is
one problem area, though: the style sheets.</p>
<p>The style sheets for the application have organically evolved over years,
written by multiple authors. While there has been some effort to scope CSS into
components, every component is a bit different and there are no clear principles
on what styles should be set where. This results in two problems:</p>
<ol>
<li>Various parts of the UI that <em>should</em> look the same are subtly different.</li>
<li>When you try to modify something or add something new, you’ll end up fighting
the cascading and the specificity of the existing styles.</li>
</ol>
<p>I imagine this is a familiar situation for many web developers! I’ve certainly
been here before. What makes it different is that this time I decided to learn
what’s the state of the art for architecting your CSS to avoid these problems.
If you are an experienced web developer, you probably already know everything in
this article.</p>
<p>The feature of CSS that makes it so easy to mess up is that there’s a single
global scope where all your styling rules are interacting. Your styles will
accidentally cascade and then you’re in trouble. The solution is to basically
create local scopes in CSS, one way or another. I learned that there are two
groups of solutions: the manual ones and the more recent automated ones.</p>
<p>The manual way is to structure your CSS in a principled way. There are a bunch
of methodologies with similar ideas: <a href="http://getbem.com/">BEM</a>, <a href="https://smacss.com">SMACSS</a>,
<a href="https://www.smashingmagazine.com/2011/12/an-introduction-to-object-oriented-css-oocss/">OOCSS</a> etc. I chose BEM, short for <em>block-element-modifier</em>, because it
is the simplest one and as such seemed like a good starting point. The basic
idea is the following:</p>
<ul>
<li>Only ever use class selectors.</li>
<li>Name your classes according to the <a href="http://getbem.com/naming/">block-element-modifier convention</a>.</li>
<li>No cascading.</li>
</ul>
<p>This ensures that the styles you want, and only those, get applied to exactly
where you want them. Based on my brief experience, it works.</p>
<p>The automated way is to write CSS as usual and then apply it only to specific
parts of your DOM tree by using tools like <a href="https://github.com/css-modules/css-modules">CSS modules</a> or Polymer’s
<a href="https://www.polymer-project.org/1.0/docs/devguide/styling">shadow DOM style encapsulation</a>. The good part is that the tools
require less manual work and less discipline than BEM. I chose to skip them this
time, because they require quite a bit of infrastructure that does not (yet)
exist in my project.</p>
<p>I’ve looked at BEM before. Back then, I thought it was suspiciously verbose and
did not see the point. I still think it’s verbose, but now I see that it’s also
useful.</p>
<p><em>Thanks to <a href="https://twitter.com/freiksenet">Mikhail</a> and <a href="https://twitter.com/ze_hilda">Santeri</a> for pointers.</em></p>
Why look beyond JavaScript?
https://quanttype.net/p/you-should-look-beyond-js/
Tue, 07 Jun 2016 00:00:00 +0000https://quanttype.net/p/you-should-look-beyond-js/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/danger_hu_d9c05defa20bbd51.webp" type="image/webp" />
<img src="https://quanttype.net/images/danger.jpg" width="600" height="337.8" loading="lazy" />
<figcaption>A dangerous slope ahead.</figcaption>
</picture>
</figure>
<p>There are a number of interesting programming languages that you could use in
the browser instead of JavaScript. <a href="https://github.com/clojure/clojurescript">ClojureScript</a>, <a href="https://www.typescriptlang.org">TypeScript</a>,
<a href="http://www.purescript.org">PureScript</a>, and <a href="http://elm-lang.org">Elm</a> are some of the prominent examples
<a href="https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-js">among many others</a>. The controversial question is: <em>why would you use
any of them instead of JavaScript?</em></p>
<p>After all, JavaScript has good tools and a huge, fast-moving ecosystem. As a
language, JavaScript used to be quite awkward, but it has improved a lot in the
recent years. Especially <a href="https://github.com/lukehoban/es6features">ECMAScript 2015</a> was a great improvement and made
JS so much nicer to write.</p>
<p>My answer is that (some of) the alternatives are <em>easier to think about</em>. You
can get features like immutable data structures as the standard, static typing,
and algebraic data types. All of these help to make the intent of your code
clear. They bring <a href="http://swannodette.github.io/2016/06/03/tools-for-thought">crispness</a> to your code.</p>
<p>You can get immutable data structures via libraries for JavaScript (e.g.
<a href="http://swannodette.github.io/mori/">mori</a> and <a href="https://facebook.github.io/immutable-js/">Immutable.js</a>) and there are static type checkers as
well (e.g. <a href="http://flowtype.org">Flow</a>). The problem is that they are not widely used. When you
want to use third-party libraries, you need convert back to mutable data and
come up with type annotations.</p>
<p>The alternative languages can also give you simpler semantics. JavaScript has
not been able to escape the complicated <code>this</code> or the uneasy use of objects as
maps. Programmers structure their code to avoid the traps, but it’s hard to
avoid them entirely. Elm is an impressive example of how you could do something
different here.</p>
<p>This is why I advocate alternatives to JavaScript. Coincidentally this is also
why I advocate functional programming techniques. Code that is easy to think
about has less bugs and is easier to change.</p>
What is descriptive set theory?
https://quanttype.net/p/descriptive-set-theory/
Tue, 24 May 2016 00:00:00 +0000https://quanttype.net/p/descriptive-set-theory/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/borel-tree_hu_805a7fd9c32529cf.webp" type="image/webp" />
<img src="https://quanttype.net/images/borel-tree.jpg" width="600" height="471.6" alt="This tree witnesses that every Borel set in Baire space contains a perfect set." loading="lazy" />
</picture>
</figure>
<p>Today I took the final exam in a course on <em>descriptive set theory</em>. Let me you
tell about it.</p>
<p>Sets are a basic structure in mathematics. They’re are very generic, so they
come up everywhere - everybody needs a collection of things, after all. Because
they’re so generic, you can’t say much about them. To do something useful with a
set, you need to know more about it. You have to add some structure.</p>
<p>My mental image of this is that a set is like a blob of jelly. If you try to
pick it up and move it somewhere, you’ll just mess up everything. Put it on a
plate, though, and it’s easy to move around.</p>
<p>One of the ways to add structure is <a href="https://en.wikipedia.org/wiki/Topology">topology</a>. In topology, you describe
the <em>open sets</em> of your space. They behave very nicely, but they have too much
structure. Not that many interesting sets are open.</p>
<p>Descriptive set theory finds a middle-ground by defining <a href="https://en.wikipedia.org/wiki/Borel_set">Borel sets</a>.
The collection of Borel sets of a topological space is the smallest collection
of sets that includes the open sets and is closed under complement and countable
unions and intersections. They’re the sets you can make out of open sets by
these basic operations. To make even more sets, you can define the
<a href="https://en.wikipedia.org/wiki/Projective_hierarchy">projective hierarchy</a> where you start with Borel sets and iteratively
project them.</p>
<p>After taking the course, I don’t know <em>where</em> you would want to use Borel sets
or projective sets, but if you do, they will behave quite well.</p>
How I solved the Orbital Challenge
https://quanttype.net/p/orbital-challenge/
Tue, 17 May 2016 00:00:00 +0000https://quanttype.net/p/orbital-challenge/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kyltit_hu_f9868001dce0c79f.webp" type="image/webp" />
<img src="https://quanttype.net/images/kyltit.jpg" width="600" height="430.8" alt="A real-world solution to a routing problem." loading="lazy" />
</picture>
</figure>
<p>Reaktor, a local IT consulting company, recently posted a programming puzzle
called <a href="https://reaktor.com/orbital-challenge/">Reaktor Orbital Challenge</a>. You’re given a starting point and
an end point on the globe and the locations for a bunch of communication
satellites. The task is to find a route from the starting point to the end point
via the satellites. Preferably the route should be the shortest possible one.
The satellites and the ground stations can only communicate if they have a
line-of-sight.</p>
<p>An Oculus Rift was drawn between the participants. This generated some buzz
among the Finnish programming community. I especially enjoyed the low-effort
solutions like copy-pasting the coordinates to Google Maps and eyeballing the
route.</p>
<p>I have a bit of a love/hate relation with this kind of recruitment puzzles: I
wouldn’t want to spend time on them, but they <a href="https://xkcd.com/356/">nerd-snipe</a> me so easily.
Therefore I decided to aim for a low-effort solution that still finds the
shortest path. Let me tell you about it.</p>
<p>I wrote my solution in Clojure and used the graph library <a href="https://github.com/aysylu/loom">Loom</a>, the
linear algebra library <a href="https://github.com/mikera/core.matrix">core.matrix</a> and the utility library
<a href="https://github.com/metosin/potpuri">potpuri</a>. You can find the full source <a href="https://gist.github.com/miikka/31c12cbc6749575d1573ea77ee39321d">here</a>.</p>
<h2 id="the-idea">The idea</h2>
<p>Finding the shortest route is an instance of the well-known problem of finding
<a href="https://en.wikipedia.org/wiki/Shortest_path_problem">the shortest path</a> between two nodes in a weighted graph. Satellites
are the nodes and they’re connected if they can see each other. The weight of an
edge is the distance between satellites. The ground stations need to be included
as well.</p>
<p>There are so few satellites that you do not need to do anything clever - using a
standard algorithm would be fine performance-wise. I looked up graph libraries
for Clojure and decided to go with Loom, which has an implementation of
<a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">Dijkstra’s algorithm</a>.</p>
<p>The locations are given in longitude/latitude/altitude coordinates. I realized
that you could do the calculations in this coordinate system, but I didn’t know
how to do it and I didn’t know if it would be easy (turns out it is). Converting
to the Euclidean coordinates is easy and I knew how to work with them, so that’s
what I did.</p>
<p>How do you build the graph, then? Calculating the weight is easy, we can just
use the <a href="https://en.wikipedia.org/wiki/Euclidean_distance">Euclidean distance</a> of the points. All that is left is to
check that the points can see each other. As we will see below, it’s not very
hard either.</p>
<h2 id="coordinate-transform">Coordinate transform</h2>
<p>Initially I did the coordinate transform the same way as I’ve done it since I
was 15 years old: by looking up <a href="https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions">rotation matrices</a> and multiplying
them. Later I realized I should’ve looked up <a href="https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates">this thing</a> instead. The
problem statement says that Earth is a perfect sphere with the radius of 6371
km. Thus in Wikipedia’s formulas, $N(\phi) = 6371\ \textrm{km}$ and $e = 0$. In
Clojure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">earth-radius</span> <span class="mi">6371</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">geo->xyz</span> <span class="p">[[</span><span class="nv">lat</span> <span class="nv">lon</span> <span class="nv">alt</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Convert latitude/longitude/altitude to Euclidean coordinates."</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">h</span> <span class="p">(</span><span class="nb">+ </span><span class="nv">earth-radius</span> <span class="nv">alt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">lat</span> <span class="p">(</span><span class="nf">to-radians</span> <span class="nv">lat</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">lon</span> <span class="p">(</span><span class="nf">to-radians</span> <span class="nv">lon</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[(</span><span class="nb">* </span><span class="nv">h</span> <span class="p">(</span><span class="nf">Math/cos</span> <span class="nv">lat</span><span class="p">)</span> <span class="p">(</span><span class="nf">Math/cos</span> <span class="nv">lon</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">* </span><span class="nv">h</span> <span class="p">(</span><span class="nf">Math/cos</span> <span class="nv">lat</span><span class="p">)</span> <span class="p">(</span><span class="nf">Math/sin</span> <span class="nv">lon</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">* </span><span class="nv">h</span> <span class="p">(</span><span class="nf">Math/sin</span> <span class="nv">lat</span><span class="p">))]))</span>
</span></span></code></pre></div><p>I chose to represent x-y-z vectors as three-element Clojure vectors. This is
both concise and understood by core.matrix.</p>
<p>When doing this conversion, you have to fix the origin and the directions for
the Euclidean axes. I did it the “obvious” way:</p>
<ol>
<li>We use right-handed coordinate system.</li>
<li>The origin is in the center of the Earth.</li>
<li>The positive Z axis goes through the North Pole.</li>
<li>The positive X axis goes through the prime meridian.</li>
</ol>
<p>When looking the formulas up, I learned that this way has a name. It’s called
<a href="https://en.wikipedia.org/wiki/ECEF"><em>Earth-Centered, Earth-Fixed</em></a> coordinate system.</p>
<h2 id="line-of-sight">Line of sight</h2>
<p>Next, we have two satellites and the Earth. How do we know if the satellites can
see each other and that the Earth is not between them?</p>
<p>One way is to write the equations for the sphere of Earth and the line segment
between the satellites and to solve for the intersection points. Another way is
to calculate the distance between the line segment and the center of Earth. If
the distance is less than Earth’s radius, there’s an intersection.</p>
<p>I always think about the distance between a line and a point as projecting the
point to the line. It was quick to write the required calculations and I even
got them right on the first try! The vector operations are kindly provided by
core.matrix.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">point-line-segment-dist</span>
</span></span><span class="line"><span class="cl"> <span class="s">"Compute the shortest distance between the line segment a-b and point c."</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">a</span> <span class="nv">b</span> <span class="nv">c</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">k</span> <span class="p">(</span><span class="nb">- </span><span class="nv">b</span> <span class="nv">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nv">l</span> <span class="p">(</span><span class="nb">- </span><span class="nv">c</span> <span class="nv">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">;; scalar projection of l onto k.</span>
</span></span><span class="line"><span class="cl"> <span class="nv">t</span> <span class="p">(</span><span class="nb">/ </span><span class="p">(</span><span class="nf">dot</span> <span class="nv">l</span> <span class="nv">k</span><span class="p">)</span> <span class="p">(</span><span class="nf">norm</span> <span class="nv">k</span><span class="p">))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">distance</span> <span class="p">(</span><span class="nb">+ </span><span class="nv">a</span> <span class="p">(</span><span class="nb">* </span><span class="p">(</span><span class="nf">clamp</span> <span class="nv">t</span> <span class="mi">0</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">k</span><span class="p">))</span> <span class="nv">c</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">line-of-sight?</span> <span class="p">[</span><span class="nv">a</span> <span class="nv">b</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb"><= </span><span class="nv">earth-radius</span> <span class="p">(</span><span class="nf">point-line-segment-dist</span> <span class="nv">a</span> <span class="nv">b</span> <span class="p">[</span><span class="mi">0</span> <span class="mi">0</span> <span class="mi">0</span><span class="p">])))</span>
</span></span></code></pre></div><h2 id="building-the-graph">Building the graph</h2>
<p>The input file looks something like this:</p>
<pre tabindex="0"><code>#SEED: 0.24904920277185738
SAT0,78.47003444920836,-80.16227317274806,556.3585069486544
SAT1,18.2619020748309,89.92023247596006,367.93737770788107
# ...lines omitted...
SAT19,43.337390738091216,117.63969735544856,541.2530580475883
ROUTE,-44.35263069870813,162.23137604080517,62.2777848501151,-90.67241530334475
</code></pre><p>I’m not going to bore you with the parsing code. I parsed the input into a map
like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">{</span><span class="s">"SAT0"</span> <span class="p">[</span><span class="mf">78.47003444920836</span> <span class="mf">-80.16227317274806</span> <span class="mf">556.3585069486544</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="c1">;; ...lines omitted...</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:start</span> <span class="p">[</span><span class="mf">-44.35263069870813</span> <span class="mf">162.23137604080517</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:end</span> <span class="p">[</span><span class="mf">62.2777848501151</span> <span class="mf">-90.67241530334475</span> <span class="mi">0</span><span class="p">]}</span>
</span></span></code></pre></div><p>Now all that is needed is a sequence of edges and weights for Loom:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">build-graph</span> <span class="p">[</span><span class="nv">data</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">xyz-data</span> <span class="p">(</span><span class="nf">map-vals</span> <span class="nv">geo->xyz</span> <span class="nv">data</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">apply </span><span class="nv">weighted-graph</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">for </span><span class="p">[[</span><span class="nv">key1</span> <span class="nv">pos1</span><span class="p">]</span> <span class="nv">xyz-data</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">key2</span> <span class="nv">pos2</span><span class="p">]</span> <span class="nv">xyz-data</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:when</span> <span class="p">(</span><span class="nb">and </span><span class="p">(</span><span class="nb">not= </span><span class="nv">key1</span> <span class="nv">key2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">not= </span><span class="o">#</span><span class="p">{</span><span class="ss">:start</span> <span class="ss">:end</span><span class="p">}</span> <span class="p">(</span><span class="nb">set </span><span class="p">[</span><span class="nv">key1</span> <span class="nv">key2</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">line-of-sight?</span> <span class="nv">pos1</span> <span class="nv">pos2</span><span class="p">))]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">key1</span> <span class="nv">key2</span> <span class="p">(</span><span class="nf">distance</span> <span class="nv">pos1</span> <span class="nv">pos2</span><span class="p">)]))))</span>
</span></span></code></pre></div><p>Our work here is done.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">get-route</span> <span class="p">[</span><span class="nv">graph</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">butlast </span><span class="p">(</span><span class="nb">rest </span><span class="p">(</span><span class="nf">dijkstra-path</span> <span class="nv">graph</span> <span class="ss">:start</span> <span class="ss">:end</span><span class="p">))))</span>
</span></span></code></pre></div><p>For the full solution, see <a href="https://gist.github.com/miikka/31c12cbc6749575d1573ea77ee39321d">my gist</a>. It took me 80 lines of Clojure and
about an hour, so the effort was sufficiently low.</p>
<h2 id="see-also">See also</h2>
<p>Unlike me, Jouni Seppänen knows how to do the calculations in the spherical
coordinates. I recommend checking out his <a href="https://gist.github.com/jkseppan/c12a3877d5e5ca6c358e2a1bf5a01afd">Python solution</a>.</p>
Mamiya RB67 Pro-S
https://quanttype.net/p/mamiya-rb67/
Tue, 10 May 2016 00:00:00 +0000https://quanttype.net/p/mamiya-rb67/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mamiya-rb67_hu_6a92f579191d210d.webp" type="image/webp" />
<img src="https://quanttype.net/images/mamiya-rb67.jpg" width="600" height="600" loading="lazy" />
</picture>
</figure>
<p>I thought I wouldn’t write about cameras on this blog, but I’ve been shooting
with such a cool camera that I want to tell you about it. It’s a Mamiya RB67
Pro-S with a 127 mm lens, lent to me by a friend.</p>
<p>RB67 is a medium format SLR camera from the 1970s. <em>Medium format</em> means it
takes 6x7 cm photos - this is in contrast to the smaller 35 mm format and to the
larger 4x5 inch format. It’s actually a camera system - in addition to the lens,
the film back and the finder are interchangeable. My friend has only got the one
lens and the waist-level finder, though.</p>
<p>Taking a picture with the camera is quite involved operation compared to the
modern cameras. You have to take out the dark slide that protects the film back
from light. You have to turn a lever to cock the shutter and after taking the
picture, you get to turn another lever to wind the film. You can rotate the film
back, so you can take both horizontal and vertical pictures without rotating the
whole camera.</p>
<p>There’s no light meter, either. I’ve got a vintage light meter, but I’m not sure
how reliable it is, so I’ve been relying on my other cameras’ light meters. It
has worked fine so far. The film backs I’ve got take 120 roll film. Luckily it’s
still widely available, although it’s not as common as 35 mm film. The
photography shops will develop it as well.</p>
<p>I like the waist-level finder a lot. <em>Waist-level</em> means that you look at it
from above. I don’t know why, but <em>everybody looks so great through the finder
that I just have to take a picture of them</em>. Maybe it’s because I’m tall, so my
waist level is more suitable for photographing people than my eye level.</p>
<p>The camera is so bulky that I assume it was meant to be used with a tripod.
Probably it was more at home in a studio than on the street. Lugging around a
tripod is not my thing, so I got a neck strap and have been shooting outdoors.
(<a href="http://optechusa.com/super-pro-strap.html">OP/Tech Super Prop Strap</a> with the B type connector fits the camera.)</p>
<p>So far a shop has developed the films for me and I’ve got only proofs made.
Eventually I’d like to get a big print of some photo I’ve taken with this
camera. Unfortunately this means you get to enjoy scans of the proofs. Oh well.</p>
<p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mf1_hu_a89cc36e58f92788.webp" type="image/webp" />
<img src="https://quanttype.net/images/mf1.jpg" width="600" height="504.3103448275862" loading="lazy" />
</picture>
</figure>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mf2_hu_43e4e3ad3795a2f2.webp" type="image/webp" />
<img src="https://quanttype.net/images/mf2.jpg" width="600" height="526.1744966442953" loading="lazy" />
</picture>
</figure>
</p>
<p>I’ve had some problems, though. In the last two rolls, some of the pictures had
over-exposed bands like this. The band is always in the same part of the frame,
but it’s not always fully burnt-out. Maybe it’s a hardware problem, or maybe
I’ve made some mistake. If you have any ideas, <a href="https://miikka.me/">let me know</a>.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/band_hu_df70dae37cd1cd73.webp" type="image/webp" />
<img src="https://quanttype.net/images/band.jpg" width="600" height="700.1293661060802" loading="lazy" />
</picture>
</figure>
<p>Anyway, medium format is cool.</p>
What's the point of dependent types?
https://quanttype.net/p/dependent-types/
Tue, 03 May 2016 00:00:00 +0000https://quanttype.net/p/dependent-types/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/trees_hu_f26da3c5a50759c1.webp" type="image/webp" />
<img src="https://quanttype.net/images/trees.jpg" width="600" height="400.2" loading="lazy" />
</picture>
</figure>
<p>There are many languages for telling computers what to do. In some languages,
you have to tell the computer what kind of things it can use to do something.
Often this is good, like when stopping the computer from confusing numbers with
words.</p>
<p>In most languages you can say simple things like “this is a number” or “this is
a group of words”. The computer will check that what you say is true before it
does anything.</p>
<p>In some languages, you can say much more. You can say things like “this is a
number and it is bigger than zero” or “if this thing a group of words, then this
other thing must be a group of words with an extra word”. Again the computer
will check this. This means you can better prove that computer will do what you
think it should do.</p>
<p>This is good, because this way you can be sure that computer does what you want.
The problem is that so far it is hard work to make the computer understand what
you want. Only people who have trained a lot can do it.</p>
<hr>
<p>This was an attempt to briefly explain the point of dependently typed
programming languages using only the 1000 most common English words (according
to <a href="https://github.com/mortenjust/cleartext-mac">Cleartext</a>).</p>
We're in early days of software engineering
https://quanttype.net/p/software-engineering/
Mon, 25 Apr 2016 00:00:00 +0000https://quanttype.net/p/software-engineering/<p>Whenever there’s a discussion about whether software development is engineering
or not, somebody brings up the fact that we software developers mostly do not
know what were doing, unlike bridge builders. How do we know that bridge
builders know what they’re doing? It’s because bridges mostly do not collapse,
unlike software projects.</p>
<p>Civil engineering is not the only kind of engineering, though. For example,
there’s nuclear engineering.</p>
<p>Wikipedia has some interesting articles about accidents in early days nuclear
engineering. See for example the <a href="https://en.wikipedia.org/wiki/Demon_core">demon core incidents</a>, where two
scientists killed themselves by performing measurements on an almost-critical
sphere of plutonium. Or see the <a href="https://en.wikipedia.org/wiki/Windscale_fire">Windscale fire</a>, where it took
almost 48 hours for the operators to confirm that their nuclear reactor was on
fire.</p>
<p>By today’s standards these the incidents seem almost absurd, yet as far as I can
tell, the Los Alamos scientists as well the Windscale designers and operators
were well-educated experts.</p>
<p>Sometimes I feel that software engineering is like early nuclear engineering. At
least we only leak data, not radioactive material.</p>
Finding that lemma: Coq search commands
https://quanttype.net/p/finding-that-lemma/
Tue, 19 Apr 2016 00:00:00 +0000https://quanttype.net/p/finding-that-lemma/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/needle_hu_9feec195cc641212.webp" type="image/webp" />
<img src="https://quanttype.net/images/needle.jpg" width="600" height="337.2" loading="lazy" />
<figcaption>No hay in this needlestack.</figcaption>
</picture>
</figure>
<p>One of the hurdles in using Coq is finding the suitable lemmas from
<a href="https://coq.inria.fr/distrib/current/stdlib/">the standard library</a>. There are lots of them and while the naming is
consistent, it’s hard to remember all of them. Luckily Coq has search
commands to help you out.</p>
<p><em>Note:</em> The following commands work only on modules you have required. If a
lemma exists, but you haven’t required its module, you’re out of luck. Also,
before Coq 8.5 <code>Search</code> was called <code>SearchAbout</code> and <code>SearchHead</code> was called
<code>Search</code>.</p>
<p>The simplest way to search is to search by name. This is one of the things
<a href="https://coq.inria.fr/distrib/current/refman/proof-engine/vernacular-commands.html#coq:cmd.Search"><code>Search</code></a> command does:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="kn">Search</span> <span class="s2">"len"</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="n">length</span><span class="o">:</span> <span class="k">forall</span> <span class="n">A</span> <span class="o">:</span> <span class="kt">Type</span><span class="o">,</span> <span class="kt">list</span> <span class="n">A</span> <span class="o">-></span> <span class="kt">nat</span>
</span></span></code></pre></div><p>You can also search for theorems (or other objects) whose statement contains a
given identifier.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="kn">Search</span> <span class="n">False</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="n">False_rect</span><span class="o">:</span> <span class="k">forall</span> <span class="n">P</span> <span class="o">:</span> <span class="kt">Type</span><span class="o">,</span> <span class="n">False</span> <span class="o">-></span> <span class="n">P</span>
</span></span><span class="line"><span class="cl"><span class="n">False_ind</span><span class="o">:</span> <span class="k">forall</span> <span class="n">P</span> <span class="o">:</span> <span class="kt">Prop</span><span class="o">,</span> <span class="n">False</span> <span class="o">-></span> <span class="n">P</span>
</span></span><span class="line"><span class="cl"><span class="c">(* ... *)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="kn">Search</span> <span class="n">0</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="n">nat_rect</span><span class="o">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="n">P</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">-></span> <span class="kt">Type</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">P</span> <span class="n">0</span> <span class="o">-></span> <span class="o">(</span><span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">P</span> <span class="n">n</span> <span class="o">-></span> <span class="n">P</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">))</span> <span class="o">-></span> <span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">P</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"><span class="n">nat_ind</span><span class="o">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="n">P</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">-></span> <span class="kt">Prop</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">P</span> <span class="n">0</span> <span class="o">-></span> <span class="o">(</span><span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">P</span> <span class="n">n</span> <span class="o">-></span> <span class="n">P</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">))</span> <span class="o">-></span> <span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">P</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"><span class="c">(* ... *)</span>
</span></span></code></pre></div><p>Another thing you can do is to search for patterns with holes <code>_</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="kn">Search</span> <span class="o">(</span><span class="n">S</span> <span class="o">_</span> <span class="o"><=</span> <span class="o">_).</span>
</span></span><span class="line"><span class="cl"><span class="n">le_S_n</span><span class="o">:</span> <span class="k">forall</span> <span class="n">n</span> <span class="n">m</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">S</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">S</span> <span class="n">m</span> <span class="o">-></span> <span class="n">n</span> <span class="o"><=</span> <span class="n">m</span>
</span></span><span class="line"><span class="cl"><span class="n">le_n_S</span><span class="o">:</span> <span class="k">forall</span> <span class="n">n</span> <span class="n">m</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">m</span> <span class="o">-></span> <span class="n">S</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">S</span> <span class="n">m</span>
</span></span></code></pre></div><p>When searching for a pattern, <code>Search</code> matches anywhere in the statement. If you
only want to search for the pattern in the conclusion, use
<a href="https://coq.inria.fr/distrib/current/refman/proof-engine/vernacular-commands.html#coq:cmd.SearchPattern"><code>SearchPattern</code></a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="n">SearchPattern</span> <span class="o">(</span><span class="n">S</span> <span class="o">_</span> <span class="o"><=</span> <span class="o">_).</span>
</span></span><span class="line"><span class="cl"><span class="n">le_n_S</span><span class="o">:</span> <span class="k">forall</span> <span class="n">n</span> <span class="n">m</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">m</span> <span class="o">-></span> <span class="n">S</span> <span class="n">n</span> <span class="o"><=</span> <span class="n">S</span> <span class="n">m</span>
</span></span></code></pre></div><p>If you’re looking for a rewrite, there’s <a href="https://coq.inria.fr/distrib/current/refman/proof-engine/vernacular-commands.html#coq:cmd.SearchRewrite"><code>SearchRewrite</code></a>. It
finds conclusions of type <code>_ = _</code> where one of the sides matches the given
pattern.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Coq</span> <span class="o"><</span> <span class="n">SearchRewrite</span> <span class="o">(_</span> <span class="o">+</span> <span class="n">0</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"><span class="n">plus_n_O</span><span class="o">:</span> <span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">n</span> <span class="o">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">0</span>
</span></span></code></pre></div><p>As always, see <a href="https://coq.inria.fr/distrib/current/refman/proof-engine/vernacular-commands.html#coq:cmd.Search">the manual</a> for details. Coq’s manual looks
intimidating, but it does contain a lot of good information.</p>
How to divide by zero?
https://quanttype.net/p/division-by-zero/
Tue, 12 Apr 2016 00:00:00 +0000https://quanttype.net/p/division-by-zero/<p>What’s the benefit of dependent types, anyway? <a href="https://twitter.com/pyrtsa">Pyry</a> pointed this out to
me: they allow you to make your functions total by moving the preconditions to
the caller side.</p>
<p>You often end up with partial functions because of some preconditions. For
example, you might write an integer division function, but division by zero
isn’t defined. How do you handle this? In Haskell, you get a runtime exception.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="nf">λ</span><span class="o">></span> <span class="mi">1</span> <span class="p">`</span><span class="n">div</span><span class="p">`</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="o">***</span> <span class="kt">Exception:</span> <span class="n">divide</span> <span class="n">by</span> <span class="n">zero</span>
</span></span><span class="line"><span class="cl"><span class="nf">λ</span><span class="o">></span> <span class="mi">1</span> <span class="p">`</span><span class="n">rem</span><span class="p">`</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="o">***</span> <span class="kt">Exception:</span> <span class="n">divide</span> <span class="n">by</span> <span class="n">zero</span>
</span></span></code></pre></div><p>Elm <a href="https://quanttype.net/posts/2016-03-22-runtime-exceptions-in-elm.html">tries to avoid runtime exceptions</a> and it makes division total by
extending the usual definition of division:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elm" data-lang="elm"><span class="line"><span class="cl"><span class="nf">></span> <span class="mi">1</span> <span class="nf">//</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="mi">0</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span><span class="line"><span class="cl"><span class="nf">></span> <span class="mi">1</span> <span class="nf">`</span><span class="nv">rem</span><span class="nf">`</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="kt">NaN</span> <span class="nf">:</span> <span class="kt">Int</span>
</span></span></code></pre></div><p>The unorthodox result for <code>1 `rem` 0</code> is
<a href="https://github.com/elm-lang/elm-compiler/issues/1338">likely a bug</a>. This
solution quietly breaks the invariant <code>x == (x // y) * y + x `rem` y</code>, but
it’s not a big deal. Coq does the same thing. Another solution would be to make
the division function return <code>Maybe</code>. In Haskell:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="nf">safeDiv</span> <span class="ow">::</span> <span class="kt">Integral</span> <span class="n">a</span> <span class="ow">=></span> <span class="n">a</span> <span class="ow">-></span> <span class="n">a</span> <span class="ow">-></span> <span class="kt">Maybe</span> <span class="n">a</span>
</span></span><span class="line"><span class="cl"><span class="nf">safeDiv</span> <span class="n">a</span> <span class="mi">0</span> <span class="ow">=</span> <span class="kt">Nothing</span>
</span></span><span class="line"><span class="cl"><span class="nf">safeDiv</span> <span class="n">a</span> <span class="n">b</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="p">(</span><span class="n">a</span> <span class="p">`</span><span class="n">div</span><span class="p">`</span> <span class="n">b</span><span class="p">)</span>
</span></span></code></pre></div><p>This means that you have to lift all your division-using computations into
Maybe. A language like Coq offers you yet another possibility: you can demand
that the caller proves that the divisor is non-zero:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Require</span> <span class="kn">Import</span> <span class="n">Arith</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Require</span> <span class="kn">Import</span> <span class="n">Nat</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">safeDiv</span> <span class="n">x</span> <span class="o">(</span><span class="n">y</span> <span class="o">:</span> <span class="o">{</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">|</span> <span class="n">0</span> <span class="o"><</span> <span class="n">n</span> <span class="o">})</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">:=</span>
</span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">y</span> <span class="k">with</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">exist</span> <span class="o">_</span> <span class="n">O</span> <span class="n">pf</span> <span class="o">=></span> <span class="k">match</span> <span class="n">lt_irrefl</span> <span class="o">_</span> <span class="n">pf</span> <span class="k">with</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">exist</span> <span class="o">_</span> <span class="o">(</span><span class="n">S</span> <span class="n">y'</span><span class="o">)</span> <span class="o">_</span> <span class="o">=></span> <span class="n">div</span> <span class="n">x</span> <span class="o">(</span><span class="n">S</span> <span class="n">y'</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span>
</span></span></code></pre></div><p>This is a total function. If you want to call it, you have to do it along with a
proof that y is non-zero. For example, divide 3 by 2:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Example</span> <span class="n">div_3_2</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">:=</span> <span class="n">safeDiv</span> <span class="n">3</span> <span class="o">(</span><span class="n">exist</span> <span class="o">_</span> <span class="n">2</span> <span class="n">Nat</span><span class="o">.</span><span class="n">lt_0_2</span><span class="o">).</span>
</span></span><span class="line"><span class="cl"><span class="n">Eval</span> <span class="k">compute</span> <span class="k">in</span> <span class="n">div_3_2</span><span class="o">.</span> <span class="c">(* = 1 : nat *)</span>
</span></span></code></pre></div><p>We could try dividing 3 by 0. Let’s do it in type-driven style with the
<a href="https://coq.inria.fr/distrib/current/refman/Reference-Manual010.html#hevea_tactic7"><code>refine</code></a> tactic. It allows us to leave holes (<code>_</code>) in a term and fill
them using Coq’s goal mechanism:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Example</span> <span class="n">div_3_0</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">refine</span> <span class="o">(</span><span class="n">safeDiv</span> <span class="n">3</span> <span class="o">(</span><span class="n">exist</span> <span class="o">_</span> <span class="n">0</span> <span class="o">_)).</span>
</span></span></code></pre></div><p>Here’s the goal we get:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">1</span> <span class="n">subgoal</span><span class="o">,</span> <span class="n">subgoal</span> <span class="n">1</span> <span class="o">(</span><span class="n">ID</span> <span class="n">9</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="o">============================</span>
</span></span><span class="line"><span class="cl"> <span class="n">0</span> <span class="o"><</span> <span class="n">0</span>
</span></span></code></pre></div><p>Good luck with that.</p>
Take better photos by looking carefully
https://quanttype.net/p/better-photos-by-looking-carefully/
Tue, 05 Apr 2016 00:00:00 +0000https://quanttype.net/p/better-photos-by-looking-carefully/<p>Internet is full of advice about the technical aspects of photography, but it’s
much harder to find good advice on the artistic aspects. Therefore I’ve come up
with some advice of my own.</p>
<p>My friends tell me that a fundamental part of learning to paint or draw is to
learn to <em>see</em> the scene properly. You may think you know what a scene looks
like, but when you try to draw it, you quickly notice how poorly you have
observed it. This is one of the attractions of drawing a live model: it’s very
easy to notice that your drawing does not match what you’re seeing.</p>
<p>I’ve been mindful about this while photographing and it has helped me. For
example, my film photos are better than my digital ones. The main reason is that
I’m so slow at operating my film cameras that I end up looking at the scene more
carefully while shooting, leading to better pictures.</p>
<p><strong>Spend more time looking at what you’re shooting.</strong> And do not just look at the
scene - <em>see</em> it. What do you see? How is the light? What makes the scene
interesting? What are you trying to capture?</p>
<p><strong>Spend more time looking at what you’ve shot.</strong> Does it match what you saw in
the scene? What makes the picture interesting?</p>
defaultdicts all the way down
https://quanttype.net/p/defaultdicts-all-the-way-down/
Tue, 29 Mar 2016 00:00:00 +0000https://quanttype.net/p/defaultdicts-all-the-way-down/<p>You may know the Haskell function <code>fix</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="ow">::</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-></span> <span class="n">a</span><span class="p">)</span> <span class="ow">-></span> <span class="n">a</span>
</span></span><span class="line"><span class="cl"><span class="nf">fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="kr">in</span> <span class="n">x</span>
</span></span></code></pre></div><p>This function applies its argument to itself. It’s called <code>fix</code> because it finds
a (least-defined) fixed point of the function: <code>f (fix f) == fix f</code>. Here are
some examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="o">></span> <span class="n">take</span> <span class="mi">10</span> <span class="o">$</span> <span class="n">fix</span> <span class="p">(</span><span class="mi">1</span><span class="kt">:</span><span class="p">)</span> <span class="c1">-- fix (1:) == [1,1..]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">></span> <span class="n">fix</span> <span class="p">(</span><span class="nf">\</span><span class="n">f</span> <span class="n">n</span> <span class="ow">-></span> <span class="kr">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">0</span> <span class="kr">then</span> <span class="mi">1</span> <span class="kr">else</span> <span class="n">n</span> <span class="o">*</span> <span class="n">f</span> <span class="p">(</span><span class="n">pred</span> <span class="n">n</span><span class="p">))</span> <span class="mi">5</span> <span class="c1">-- factorial</span>
</span></span><span class="line"><span class="cl"><span class="mi">120</span>
</span></span></code></pre></div><p>It’s not a function you need very often, but the other day I needed it in
Python! I wanted to have a <a href="https://docs.python.org/3/library/collections.html#collections.defaultdict">defaultdict</a> that defaults to defaultdict
that defaults to defaultdict that defaults… all the way down. This is a fixed
point of defaultdict. Here we go:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fix</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">lambda</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">f</span><span class="p">(</span><span class="n">fix</span><span class="p">(</span><span class="n">f</span><span class="p">),</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span></code></pre></div><p>We have to wrap <code>f</code> inside a lambda so that it’s not evaluated
when <code>fix</code> is called. Let’s try it out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">>>></span> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">d</span> <span class="o">=</span> <span class="n">fix</span><span class="p">(</span><span class="n">defaultdict</span><span class="p">)()</span>
</span></span><span class="line"><span class="cl"><span class="o">>>></span> <span class="n">d</span><span class="p">[</span><span class="s2">"a"</span><span class="p">][</span><span class="s2">"b"</span><span class="p">][</span><span class="s2">"c"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">defaultdict</span><span class="p">(</span><span class="o"><</span><span class="n">function</span> <span class="o"><</span><span class="k">lambda</span><span class="o">></span> <span class="n">at</span> <span class="mh">0x105c4bed8</span><span class="o">></span><span class="p">,</span> <span class="p">{})</span>
</span></span></code></pre></div><p>You can bet I was feeling clever when I wrote this.</p>
Runtime exceptions in Elm
https://quanttype.net/p/runtime-exceptions-in-elm/
Tue, 22 Mar 2016 00:00:00 +0000https://quanttype.net/p/runtime-exceptions-in-elm/<p>Today was the first <a href="http://www.meetup.com/Elmsinki/">Elmsinki</a> meetup, where we gathered to discuss
<a href="http://elm-lang.org">Elm</a> the programming language. <a href="http://ohanhi.github.io">Ossi</a> gave us a quick introduction
to Elm. One of the points in his Elm elevator pitch was that <em>there are no
runtime exceptions</em>. I asked what this means, but ultimately misunderstood the
explanation. After thinking it through, here’s my current understanding:</p>
<p><strong>Elm does not have an exception system.</strong> There’s no mechanism for throwing and
catching exceptions. You might be able to build one, though.</p>
<p><strong>You can have runtime errors in Elm.</strong> For example, there’s <code>Debug.crash : String -> a</code>, which is equivalent to Haskell’s <code>error :: String -> a</code>. They both
abort the computation - there’s no way to handle the error. You can use this to
define partial functions:</p>
<pre tabindex="0"><code>unsafeHead x =
case x of
(y :: _) -> y
_ -> Debug.crash "oh no :("
</code></pre><p>There are also some other ways to get a runtime error, like running out of stack:</p>
<pre tabindex="0"><code>> g x = 0 + g x
<function> : a -> number
> g 0
RangeError: Maximum call stack size exceeded
</code></pre><p><strong>You won’t have pattern matching errors in Elm.</strong> You have to always handle all
the cases. We might try to define <code>unsafeHead</code> like this:</p>
<pre tabindex="0"><code>unsafeHead (x :: _) = x
</code></pre><p>The Elm compiler does not accept this and prints an error message:</p>
<pre tabindex="0"><code>This pattern does not cover all possible inputs.
6│ unsafeHead (x :: _) = x
^^^^^^
You need to account for the following values:
[]
</code></pre><p>When I heard “no runtime exceptions”, I first thought of total languages. Elm
clearly isn’t one. I guess “no runtime exceptions” means “runtime
exceptions/errors are rare”. Fair enough.</p>
Multitenant Flask-SQLAlchemy
https://quanttype.net/p/flask-sqlalchemy-and-multitenancy/
Tue, 15 Mar 2016 00:00:00 +0000https://quanttype.net/p/flask-sqlalchemy-and-multitenancy/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/flask_hu_aaccc37b9a06bddd.webp" type="image/webp" />
<img src="https://quanttype.net/images/flask.jpg" width="600" height="338.4" loading="lazy" />
<figcaption>Just one flask. The multiple databases are not pictured.</figcaption>
</picture>
</figure>
<p>So you’re writing a web backend with <a href="http://flask.pocoo.org">Flask</a> and <a href="http://flask-sqlalchemy.pocoo.org/">Flask-SQLAlchemy</a>.
Now you want to make the same backend connect to different databases based on
the request parameters. What do you do?</p>
<p>Flask-SQLAlchemy supports multiple databases through the <a href="http://flask-sqlalchemy.pocoo.org/2.1/binds/">bind mechanism</a>.
The binds allow you to specify in which database each table lives. What I want
to do is to choose the database for all the tables in one go but on per-request
basis.</p>
<p>I couldn’t find a definitive solution from the Internet, so I’ll share what I
came up with. Here is a small extension of Flask-SQLAlchemy that allows you to
(ab)use binds for this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">g</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">flask_sqlalchemy</span> <span class="kn">import</span> <span class="n">SQLAlchemy</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MultiTenantSQLAlchemy</span><span class="p">(</span><span class="n">SQLAlchemy</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">choose_tenant</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">bind_key</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s1">'tenant'</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s1">'Switching tenant in the middle of the request.'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">g</span><span class="o">.</span><span class="n">tenant</span> <span class="o">=</span> <span class="n">bind_key</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">get_engine</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">bind</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">bind</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s1">'tenant'</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s1">'No tenant chosen.'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">bind</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">tenant</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_engine</span><span class="p">(</span><span class="n">app</span><span class="o">=</span><span class="n">app</span><span class="p">,</span> <span class="n">bind</span><span class="o">=</span><span class="n">bind</span><span class="p">)</span>
</span></span></code></pre></div><p>We essentially have a per-request default bind for all the tables without a bind
key. Now, before you do any database queries, do <code>db.choose_tenant(name)</code>. This
tells SQLAlchemy which bind to use. For example, you could implement the tenant
choosing logic in the <code>@app.before_request</code> hook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'SQLALCHEMY_BINDS'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'test1'</span><span class="p">:</span> <span class="s1">'sqlite:///test1.db'</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s1">'test2'</span><span class="p">:</span> <span class="s1">'sqlite:///test2.db'</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">db</span> <span class="o">=</span> <span class="n">MultiTenantSQLAlchemy</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@app.before_request</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">before_request</span><span class="p">():</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># Just use the query parameter "tenant"</span>
</span></span><span class="line"><span class="cl"> <span class="n">db</span><span class="o">.</span><span class="n">choose_tenant</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="s1">'tenant'</span><span class="p">])</span>
</span></span></code></pre></div><p>Now <code>http://localhost:5000/?tenant=test1</code> goes to <code>test1.db</code> and
<code>http://localhost:5000/?tenant=test2</code> goes to <code>test2.db</code>.</p>
<p>It was surprisingly simple to make this work. Making <a href="https://alembic.readthedocs.org/">Alembic</a> work
with this is left as an exercise for the reader.</p>
<p><a href="https://gist.github.com/miikka/28a7bd77574a00fcec8d">The full source for the demo</a> is available.</p>
What is forcing, anyway?
https://quanttype.net/p/what-is-forcing/
Tue, 08 Mar 2016 00:00:00 +0000https://quanttype.net/p/what-is-forcing/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/brook_hu_79a5601dd642dd5c.webp" type="image/webp" />
<img src="https://quanttype.net/images/brook.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>Water forces its way through the forest in Nuuksio.</figcaption>
</picture>
</figure>
<p>Today was the final exam in a course on <em>set-theoretical forcing</em>. It was one of
the hardest courses I’ve attended, but at least the exam was easy. But what the
heck is forcing anyway?</p>
<p>It’s a technique for independence proofs. It was originally developed by
Paul Cohen for proving the independence of Continuum Hypothesis (CH) from
Zermelo-Fraenkel set theory with the Axiom of Choice (ZFC).</p>
<p>A theory is <em>consistent</em> if it does not allow contradictions. For example, ZFC
is thought to be consistent (although you can’t prove it in ZFC), so you can’t
derive a contradiction from the axioms of ZFC.</p>
<p>An axiom is <em>independent</em> of a theory if you can’t prove or disprove it from the
theory. You can prove the independence by showing that the theory is consistent
with the axiom and with the negation of the axiom. Assuming the consistency of
ZFC, you can prove that ZFC together with CH is consistent. Using forcing, you
can also prove that ZFC together with the negation of CH is consistent. Thus CH
is independent of ZFC.</p>
<p>How does this work in practice? We assume the existence of countable transitive
model of ZFC, $V$. Then we come up with a partially-ordered set (<em>forcing
poset</em>) that is used to construct a generic extension of the model, $V[G]$.
This model is constructed so that it witnesses whatever we want to prove. Its
existence proves the claim.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>To prove that ZFC is consistent with the negation of continuum hypothesis, i.e.
$2^\omega > \omega_1$, we would take a cardinal $\kappa$ that is larger than
$\omega_1$ in $V$. We then construct $V[G]$ so that there are at least $\kappa$
subsets of $\omega$. Since $V$ and $V[G]$ have the same cardinals, $2^\omega >
\omega$.</p>
<p>The tricky part is finding a suitable forcing poset. One of the ways to make it
easier is to use <em>iterated forcing</em>, where you repeat the forcing transfinite
number of times. I’d tell you how it works, but unfortunately I don’t understand
it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I definitely do not understand this part, but I trust the authorities. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Elementary algebra in Coq: Trivial group
https://quanttype.net/p/trivial-group/
Tue, 01 Mar 2016 00:00:00 +0000https://quanttype.net/p/trivial-group/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/stairs_hu_f97614ed095fab0c.webp" type="image/webp" />
<img src="https://quanttype.net/images/stairs.jpg" width="600" height="337.8" loading="lazy" />
<figcaption>In this series, I’m taking small steps towards understanding Coq.</figcaption>
</picture>
</figure>
<p>Recall our definition of groups in Coq from the <a href="https://quanttype.net/posts/2016-02-16-elementary-algebra.html">last time</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Structure</span> <span class="n">group</span> <span class="o">:=</span>
</span></span><span class="line"><span class="cl"> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">G</span> <span class="o">:></span> <span class="kn">Set</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">id</span> <span class="o">:</span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">inv</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">op_assoc</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="n">y</span> <span class="n">z</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">op</span> <span class="n">x</span> <span class="o">(</span><span class="n">op</span> <span class="n">y</span> <span class="n">z</span><span class="o">)</span> <span class="o">=</span> <span class="n">op</span> <span class="o">(</span><span class="n">op</span> <span class="n">x</span> <span class="n">y</span><span class="o">)</span> <span class="n">z</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op_inv_l</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">id</span> <span class="o">=</span> <span class="n">op</span> <span class="o">(</span><span class="n">inv</span> <span class="n">x</span><span class="o">)</span> <span class="n">x</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op_id_l</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">x</span> <span class="o">=</span> <span class="n">op</span> <span class="n">id</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="o">}.</span>
</span></span></code></pre></div><p>This is a record. You can construct a value of type <code>group</code> by providing a value
for all the fields. By default, the constructor is called <code>Build_group</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">Build_group</span>
</span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">G</span> <span class="o">:</span> <span class="kn">Set</span><span class="o">)</span> <span class="o">(</span><span class="n">id</span> <span class="o">:</span> <span class="n">G</span><span class="o">)</span> <span class="o">(</span><span class="n">op</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">)</span> <span class="o">(</span><span class="n">inv</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="k">forall</span> <span class="n">x</span> <span class="n">y</span> <span class="n">z</span> <span class="o">:</span> <span class="n">G</span><span class="o">,</span> <span class="n">op</span> <span class="n">x</span> <span class="o">(</span><span class="n">op</span> <span class="n">y</span> <span class="n">z</span><span class="o">)</span> <span class="o">=</span> <span class="n">op</span> <span class="o">(</span><span class="n">op</span> <span class="n">x</span> <span class="n">y</span><span class="o">)</span> <span class="n">z</span><span class="o">)</span> <span class="o">-></span>
</span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">,</span> <span class="n">op</span> <span class="o">(</span><span class="n">inv</span> <span class="n">x</span><span class="o">)</span> <span class="n">x</span> <span class="o">=</span> <span class="n">id</span><span class="o">)</span> <span class="o">-></span>
</span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">,</span> <span class="n">op</span> <span class="n">id</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">)</span> <span class="o">-></span> <span class="n">group</span>
</span></span></code></pre></div><p>This is also the reason why we didn’t include the right-hand side versions of
<code>op_inv_l</code> and <code>op_id_l</code> into the definition of <code>group</code>. If we did, you’d have
to provide proofs of the right-hand side laws when constructing a <code>group</code> value.</p>
<p>We can construct the trivial group over <code>unit</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Example</span> <span class="n">trivial_group</span> <span class="o">:</span> <span class="n">group</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">refine</span> <span class="o">(</span><span class="n">Build_group</span> <span class="kt">unit</span> <span class="n">tt</span> <span class="o">(</span><span class="k">fun</span> <span class="o">_</span> <span class="o">_</span> <span class="o">=></span> <span class="n">tt</span><span class="o">)</span> <span class="o">(</span><span class="k">fun</span> <span class="o">_</span> <span class="o">=></span> <span class="n">tt</span><span class="o">)</span> <span class="o">_</span> <span class="o">_</span> <span class="o">_).</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span><span class="o">.</span> <span class="k">destruct</span> <span class="n">x</span><span class="o">.</span> <span class="k">trivial</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Defined</span><span class="o">.</span>
</span></span></code></pre></div><p>More interestingly, we can define the additive group of integers <code>Z</code>. Since
Coq’s standard library contains a good set of properties for <code>Z</code>, defining the
group is straightforward.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Require</span> <span class="kn">Import</span> <span class="n">Coq</span><span class="o">.</span><span class="n">ZArith</span><span class="o">.</span><span class="n">BinInt</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Open</span> <span class="kn">Scope</span> <span class="n">Z</span><span class="o">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">Example</span> <span class="n">Z_add_group</span> <span class="o">:</span> <span class="n">group</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">refine</span> <span class="o">(</span><span class="n">Build_group</span> <span class="n">Z</span> <span class="o">(</span><span class="n">0</span> <span class="o">:</span> <span class="n">Z</span><span class="o">)</span> <span class="n">Z</span><span class="o">.</span><span class="n">add</span> <span class="n">Z</span><span class="o">.</span><span class="n">opp</span> <span class="n">Z</span><span class="o">.</span><span class="n">add_assoc</span> <span class="n">Z</span><span class="o">.</span><span class="n">add_opp_diag_l</span> <span class="o">_).</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">trivial</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Defined</span><span class="o">.</span>
</span></span></code></pre></div>Please give demanding tech talks
https://quanttype.net/p/please-give-demanding-tech-talks/
Tue, 23 Feb 2016 00:00:00 +0000https://quanttype.net/p/please-give-demanding-tech-talks/<p>I recently attended a tech conference. It was a well-run event. There were talks
about a good selection of topics and all the speakers were good, even great at
presenting. The food was good and I enjoyed the after-party. Yet there was a
problem: I was bored. None of the talks went over my head.</p>
<p>This problem isn’t unique to this event. When I watch conference videos, the
situation is the same. Introductory talks are everywhere, deep talks are few and
far between.</p>
<p>I wish this wasn’t the case. When I attend a tech talk, <em>I want to struggle to
understand it</em><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. If you do not need to pay any effort to understand an idea,
are you even learning anything?</p>
<p>Tech speakers, please give demanding, ambitious talks! Tech event organizers,
please invite such talks to your event!</p>
<hr>
<p>Every time I talk about this with friends, they shrug and say “I thought it was
okay”. Maybe it’s just me.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Preferably because of the depth and breadth of the ideas, not because of
poor presentation. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Elementary algebra in Coq: Defining a group
https://quanttype.net/p/elementary-algebra/
Tue, 16 Feb 2016 00:00:00 +0000https://quanttype.net/p/elementary-algebra/<p>When I was first learning about theorem provers, one of the first things I
wanted to do was to formalize some of the things I had learned about abstract
algebra. Abstract algebra should be easy to formalize, since it’s so axiomatic.</p>
<p>How would you formalize groups, then? One of the ways is to use structures
(records):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Structure</span> <span class="n">group</span> <span class="o">:=</span>
</span></span><span class="line"><span class="cl"> <span class="o">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">G</span> <span class="o">:></span> <span class="kn">Set</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">id</span> <span class="o">:</span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">inv</span> <span class="o">:</span> <span class="n">G</span> <span class="o">-></span> <span class="n">G</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">op_assoc</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="n">y</span> <span class="n">z</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">op</span> <span class="n">x</span> <span class="o">(</span><span class="n">op</span> <span class="n">y</span> <span class="n">z</span><span class="o">)</span> <span class="o">=</span> <span class="n">op</span> <span class="o">(</span><span class="n">op</span> <span class="n">x</span> <span class="n">y</span><span class="o">)</span> <span class="n">z</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op_inv_l</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">id</span> <span class="o">=</span> <span class="n">op</span> <span class="o">(</span><span class="n">inv</span> <span class="n">x</span><span class="o">)</span> <span class="n">x</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">op_id_l</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">x</span> <span class="o">=</span> <span class="n">op</span> <span class="n">id</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl"> <span class="o">}.</span>
</span></span></code></pre></div><p>This record contains the underlying set <code>G</code>, the group operations and also
witnesses of the group axioms. Now you can state theorems for all groups with
<code>forall (X : group)</code> and you have access to the axioms. <code>:></code> means a coercion
from the group to the underlying set, so if you have group <code>X</code>, you can write
<code>g : X</code> instead of <code>g : G X</code>.</p>
<p>You can make some of the arguments implicit and define a notation to make the
theorems easier to state.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Arguments</span> <span class="n">id</span> <span class="o">{</span><span class="n">g</span><span class="o">}.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Arguments</span> <span class="n">op</span> <span class="o">{</span><span class="n">g</span><span class="o">}</span> <span class="o">_</span> <span class="o">_.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Arguments</span> <span class="n">inv</span> <span class="o">{</span><span class="n">g</span><span class="o">}</span> <span class="o">_.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">Notation</span> <span class="s2">"x <.> y"</span> <span class="o">:=</span> <span class="o">(</span><span class="n">op</span> <span class="n">x</span> <span class="n">y</span><span class="o">)</span> <span class="o">(</span><span class="n">at</span> <span class="n">level</span> <span class="n">50</span><span class="o">,</span> <span class="k">left</span> <span class="n">associativity</span><span class="o">).</span>
</span></span></code></pre></div><p>Now we can state and prove a simple theorem, namely that in all groups, $f \circ
f = f$ implies $f$ is the identity element.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">square_is_unique</span> <span class="o">(</span><span class="n">G</span> <span class="o">:</span> <span class="n">group</span><span class="o">)</span> <span class="o">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="o">(</span><span class="n">f</span> <span class="o">:</span> <span class="n">G</span><span class="o">),</span> <span class="n">f</span> <span class="o"><.></span> <span class="n">f</span> <span class="o">=</span> <span class="n">f</span> <span class="o">-></span> <span class="n">f</span> <span class="o">=</span> <span class="n">id</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">f</span> <span class="n">H1</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="o"><-</span> <span class="o">(</span><span class="n">op_id_l</span> <span class="n">G</span> <span class="n">f</span><span class="o">),</span> <span class="o"><-</span> <span class="o">(</span><span class="n">op_inv_l</span> <span class="n">G</span> <span class="n">f</span><span class="o">),</span> <span class="o"><-</span> <span class="n">op_assoc</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="n">f_equal</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="kp">assumption</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span>
</span></span></code></pre></div><p>Note that I defined <code>group</code> with only left-hand side version of <code>op_inv_l</code> and
<code>op_id_l</code>. Deriving the right-hand versions is left as an exercise for the
reader.</p>
<p><strong>Update 2016-02-27:</strong> Simplified the proof for <code>square_is_unique</code>. The original
version is <a href="https://gist.github.com/miikka/8c1c9df08115dfe56f4f">here</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You could also set up some hints for <code>auto</code> and <code>autorewrite</code> to make the
theorems easier to prove. My CoqIDE just crashed and ate my hints, so
they will have to wait for the next time. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
What's in a proof?
https://quanttype.net/p/whats-in-a-proof/
Tue, 09 Feb 2016 00:00:00 +0000https://quanttype.net/p/whats-in-a-proof/<p>Let’s work through this very simple theorem in Coq:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">plus_n_O</span> <span class="o">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">),</span> <span class="n">n</span> <span class="o">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">0</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">n</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">n</span><span class="o">;</span> <span class="k">simpl</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="kp">reflexivity</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">rewrite</span> <span class="o"><-</span> <span class="n">IHn</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span>
</span></span></code></pre></div><p>The theorem, called <code>plus_n_O</code>, states that <code>n + 0</code> equals to <code>n</code> for all
natural numbers <code>n</code> (represented by the inductive datatype <code>nat</code>).</p>
<p>The first two lines are the theorem statement and below them is the proof
script. If you look at the script, you notice that it’s similar to what you’d do
in a pen-and-paper proof: use induction on <code>n</code>, evaluate <code>+</code> in both cases, use
induction hypothesis in the inductive step.</p>
<p>Another way of viewing this code snippet is that we define a function <code>plus_n_O</code>
that, given natural number <code>n</code>, returns a value of type <code>n + 0 = n</code>. Both of
these interpretations are valid - this idea is known as <em>propositions as types</em>.</p>
<p>The proof script does not look much like a function, but it does generate one.
With the command <code>Print plus_n_O</code>, we can look at the proof object it generates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coq" data-lang="coq"><span class="line"><span class="cl"><span class="n">plus_n_O</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="k">fun</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">=></span>
</span></span><span class="line"><span class="cl"><span class="n">nat_ind</span>
</span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">fun</span> <span class="n">n0</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">=></span> <span class="n">n0</span> <span class="o">=</span> <span class="n">n0</span> <span class="o">+</span> <span class="n">0</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">eq_refl</span>
</span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">fun</span> <span class="o">(</span><span class="n">n0</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">(</span><span class="n">IHn</span> <span class="o">:</span> <span class="n">n0</span> <span class="o">=</span> <span class="n">n0</span> <span class="o">+</span> <span class="n">0</span><span class="o">)</span> <span class="o">=></span>
</span></span><span class="line"><span class="cl"> <span class="n">eq_ind</span> <span class="n">n0</span> <span class="o">(</span><span class="k">fun</span> <span class="n">n1</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">=></span> <span class="n">S</span> <span class="n">n0</span> <span class="o">=</span> <span class="n">S</span> <span class="n">n1</span><span class="o">)</span> <span class="n">eq_refl</span> <span class="o">(</span><span class="n">n0</span> <span class="o">+</span> <span class="n">0</span><span class="o">)</span> <span class="n">IHn</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">n</span>
</span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="k">forall</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">n</span> <span class="o">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">0</span>
</span></span></code></pre></div><p>What’s going on here? <code>nat_ind</code> is the induction principle for <code>nat</code>, <code>eq_refl</code>
is the constructor for the equality type <code>? = ?</code> and <code>eq_ind</code> is the induction
principle for the equality type.</p>
<pre tabindex="0"><code>Coq < Check nat_ind.
nat_ind
: forall P : nat -> Prop,
P 0 -> (forall n : nat, P n -> P (S n)) -> forall n : nat, P n
Coq < Check eq_ind.
eq_ind
: forall (A : Type) (x : A) (P : A -> Prop),
P x -> forall y : A, x = y -> P y
</code></pre><p>Let’s go through the arguments of <code>nat_ind</code> in <code>plus_n_O</code>:</p>
<ul>
<li><code>(fun n0 : nat => n0 = n0 + 0)</code>: this is the proposition we want to prove
inductively.</li>
<li><code>eq_refl</code>: this is the base case. <code>0 + 0</code> is convertible to <code>0</code>, so nothing
else is needed.</li>
<li><code>(fun (n0 : nat) (IHn : n0 = n0 + 0) => eq_ind n0 (fun n1 : nat => S n0 = S n1) eq_refl (n0 + 0) IHn)</code>: this is the inductive case. We’ve proven the proposition for
<code>n0</code> and want to prove it for <code>S n0</code>. <code>IHn</code> is the induction hypothesis.</li>
<li><code>n</code>: the final argument tells that we apply the inductive proof to <code>n</code> that
<code>plus_n_O</code> is quantified over.</li>
</ul>
<p>In the inductive case, <code>eq_ind</code> is used to rewrite <code>S n0 = S n0</code> into <code>S n0 = S (n0 + 0)</code>, which is convertible into <code>S n0 = S n0 + 0</code>, which is what we want.</p>
<hr>
<p>I’m not sure if this write-up helps anyone else, but it was helpful for me to
work through this example to better understand the relationship between a proof
script and a proof term. I recommend the exercise.</p>
Getting started with Coq
https://quanttype.net/p/getting-started-with-coq/
Tue, 02 Feb 2016 00:00:00 +0000https://quanttype.net/p/getting-started-with-coq/<p>Here are some resources for programmers who want to get started with
<a href="https://coq.inria.fr">Coq</a>:</p>
<ul>
<li>Coq is best used as an <em>interactive</em> theorem prover. When you’re learning,
you’ll want to be able to jump back and forth between the steps in your proof
scripts, comment them out etc. To be able to do that, I recommend that you use
CoqIDE instead of your usual editor and the REPL (<code>coqtop</code>). CoqIDE is not the
best editor out there, but it supports proof navigation and it’s easy to set
up. In case your usual editor is Emacs, you can use <a href="http://proofgeneral.inf.ed.ac.uk">Proof General</a> as well.</li>
<li>The best tutorial out there is the book <a href="https://www.cis.upenn.edu/~bcpierce/sf/current/index.html"><em>Software Foundations</em></a> by
Benjamin C. Pierce et al. It’s the best because it has such a good set of
exercises. To start learning Coq, start working through those exercises.</li>
<li>I’ve found Adam Chlipala’s
<a href="http://adam.chlipala.net/cpdt/"><em>Certified Programming with Dependent Types</em></a> useful as well. CPDT
places more emphasis on using powerful proof automation than SF. My gut
feeling is that SF teaches you what’s going on and CPDT teaches you how to
do things in practice.</li>
<li>Another interesting text for beginners is Ilya Sergey’s
<a href="http://ilyasergey.net/pnp/pnp.pdf"><em>Programs and Proofs</em></a>. Instead of plain Coq, it uses Ssreflect, an
extension of Coq that was developed for implementing large mathematical
proofs. I’ve used Ssreflect only a bit but it looks powerful.</li>
<li>Finally, you’ll want to keep the Coq <a href="https://coq.inria.fr/distrib/current/refman/">reference manual</a>, esp.
<a href="https://coq.inria.fr/distrib/current/refman/Reference-Manual010.html">the tactics chapter</a>, and <a href="https://coq.inria.fr/distrib/current/stdlib/">the standard library docs</a> at hand.</li>
</ul>
<p>I’ve also read parts of the <a href="https://www.labri.fr/perso/casteran/CoqArt/"><em>Coq’Art</em></a> book, but I didn’t get much out
of it, so I wouldn’t recommend that.</p>
Helsinki Haskell User Group
https://quanttype.net/p/haskhel/
Tue, 26 Jan 2016 00:00:00 +0000https://quanttype.net/p/haskhel/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/hhug_hu_cb72d74b7264a28f.webp" type="image/webp" />
<img src="https://quanttype.net/images/hhug.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>Oleg explaining Servant at Wunderdog in the January 2016 meetup.</figcaption>
</picture>
</figure>
<p>I want to remind all the programmers in Helsinki about the existence of
<a href="http://www.meetup.com/Helsinki-Haskell-Users-Group/">Helsinki Haskell User Group</a> a.k.a. HaskHEL. The group has been meeting
since 2014 and lately it has been very active, meeting once per month. Big
thanks to the organizers, especially <a href="http://oleg.fi/">Oleg</a>, and the companies that have
hosted us for making this happen.</p>
<p>The current pattern is to have presentations every other month and a pub meetup
every other. The presentations topics have included things like interesting
libraries, finger trees, and the history of functional programming (presented by
yours truly!). You do not have to be a Haskell expert to attend - usually at
least one of the talks has been geared towards beginners.</p>
<p>The best place to hear about the next meeting is <a href="http://www.meetup.com/Helsinki-Haskell-Users-Group/">the meetup page</a>, but
there’s also a Twitter account <a href="https://twitter.com/haskhel">@HaskHEL</a> and an IRC channel
<a href="http://webchat.freenode.net/?channels=%23haskhel&uio=d4"><code>#haskhel</code></a> on Freenode. January meetup was today, so the next meetup will
be in February. It will be a pub meetup.</p>
<p>I know that the organizers are always looking for new presentations and
companies interested in hosting the meetup, so if you have either of those,
please let them know. You can contact them through <a href="http://www.meetup.com/Helsinki-Haskell-Users-Group/">the meetup page</a> (or
contact <a href="https://miikka.me/">me</a> and I’ll put you in touch).</p>
<p>Personally I’d like to hear about any kind experiences of using Haskell in
real-world projects. Note that the presentations do not have to be about
Haskell, as long as they’re interesting for Haskell users - for example, I bet
many Haskellers would like to hear about Elm.</p>
<p>So - if you’re in Helsinki and interested in functional programming, see you at
HaskHEL? Oh, and the word on the street is that the next
<a href="https://twitter.com/clojurefinland">Clojure Finland</a> meetup is in the works as well. See you there as well.</p>
On Infinite Jest
https://quanttype.net/p/on-infinite-jest/
Tue, 19 Jan 2016 00:00:00 +0000https://quanttype.net/p/on-infinite-jest/<p>In December, I finally finished reading <em>Infite Jest</em>, the magnum opus of <em>David
Foster Wallace</em>. It was quite an effort: I started reading it in 2010 after
hearing about <a href="http://infinitesummer.org">Infinite Summer</a>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Why did it take so long? It’s a complex, demanding book. There are a dozen
central characters. It’s full of long-winded footnotes and invented words.
There’s no single plot - it’s more like a collection of intertwined plots. Turns
out that you won’t finish that kind of book by reading it every now and then
before you go to bed.</p>
<p>Was it worth it? It’s one of those books where the journey was more important
than the destination. I enjoyed the rich language and the humor. At times the
book has quite serious takes on addiction, its main theme. Still, as a whole, it
feels unsatisfying - not much was resolved. As a book, it’s hardly my favorite,
but reading it was one of my top book-reading experiences.</p>
<p>Would I recommend it? I was going to title this post “You should read Infinite
Jest”, but I’m not sure about that. If you’re thinking about reading the book,
try reading the first 50 pages or so. If you hate it, well, it’s not going to
get any better. If you like it, it’s probably a worthwhile read. If you decide
to go forward, here are some tips:</p>
<ul>
<li>Use (at least) two bookmarks, one for the main text and one for the footnotes.
Even better, read it as an e-book. E-book is easier to carry around as well.</li>
<li>At first, it may seem confusing, but it will start making more sense after 300
pages or so.</li>
<li>You definitely shouldn’t skip the footnotes. They are as important as the main
text.</li>
<li>Consider making notes about the characters. There are so many of them that you
will lose track of who is who.</li>
<li>Reserve time for reading.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I guess I could tell you what it is about, but really, does anyone ever
tell you what Joyce’s <em>Ulysses</em> is about? No. They will tell you that it’s a complex
book. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
The bare minimum to know about RELAX NG
https://quanttype.net/p/relax-ng/
Tue, 12 Jan 2016 00:00:00 +0000https://quanttype.net/p/relax-ng/<p>I know what you’re thinking: who uses XML these days! But maybe you do, and
maybe you want to validate your XML. One of your options is to use
<a href="http://www.relaxng.org">RELAX NG</a>. I spent a moment today to learn about it and
here’s what I know.</p>
<p>RELAX NG has two syntaxes, the XML (“regular”) syntax and the compact syntax. If
you’re writing RELAX NG by hand, you likely want to use the compact syntax. If
needed, you can use <a href="https://github.com/relaxng/jing-trang">Trang</a> to convert the compact syntax to XML. For the
record, the compact syntax looks like this:</p>
<pre><code># HTML goes something like this, I think?
element html {
attribute lang { text }?,
element head {
element title { text }
},
element body {
element h1 { text }* &
element p { text }*
}
}
</code></pre>
<p>You could check your XML file with <a href="https://github.com/relaxng/jing-trang">Jing</a>:</p>
<pre><code>jing -c your_schema.rnc your_xml_file.xml
</code></pre>
<p><a href="http://www.relaxng.org/compact-tutorial-20030326.html">RELAX NG Compact Syntax Tutorial</a> is a good source for learning. I
also wrote <a href="https://gist.github.com/miikka/b137a8705e88b0b6c164">a RELAX NG cheatsheet</a>.</p>
Yearnote 2015
https://quanttype.net/p/yearnote-2015/
Sun, 27 Dec 2015 00:00:00 +0000https://quanttype.net/p/yearnote-2015/<figure><a data-flickr-embed="true" href="https://www.flickr.com/photos/arctan/22298600722/in/dateposted-public/" title="Linnanmäki"><img src="https://farm6.staticflickr.com/5750/22298600722_cf901b1ffa_z.jpg" width="600" height="400" alt="Linnanmäki"></a><script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"></script></figure>
<p>Here’s what I did in 2015. (Previously: <a href="https://quanttype.net/posts/2015-01-05-yearnote-2014.html">2014</a>)</p>
<h2 id="working-and-studying">Working and studying</h2>
<p>In spring, I did a bunch of courses at the university. Against my plans, I
didn’t write my bachelor’s thesis and therefore I didn’t graduate as a BSc. That
is disappointing, but at least I got some MSc-level courses done and finished a
minor in cognitive science. The latter is important because I’m afraid
University of Helsinki may stop teaching cognitive science soon.</p>
<p>In summer, I returned to full-time work at ZenRobotics. There’s not much to be
said about that. In fall, I concluded that I want to move on, which led to me
quitting the company in December.</p>
<h2 id="what-went-well">What went well?</h2>
<p>In spring, I started jogging three times a week and continued for more or less
the whole year. This is probably the best thing I’ve done this year - exercising
regularly makes me feel so much better about everything.</p>
<p>I made some process in my thinking about software work. This year I realized
it’s so much more about people than it is about technology. I’m still exploring
the implications of this.</p>
<h2 id="what-didnt-go-well">What didn’t go well?</h2>
<p>In my free time, I worked on a bunch of projects, but all of them fell through
before I managed to publish anything. Either I ran out of energy to work on
them, or I ended up with a problem that I couldn’t solve.</p>
<p>Clearly working harder is not going to cut it. I’m going to prioritize getting
stuff out of the door, so my plan is to move the goalposts for my projects:</p>
<ul>
<li>Set a very achieveable first goal, so I will get at least something out before
I run out of steam.</li>
<li>If I encounter an unsolvable problem, I will work around it by changing the
goal.</li>
</ul>
<h2 id="stuff-made-by-me">Stuff made by me</h2>
<ul>
<li>Continued practicing photography. See <a href="https://www.flickr.com/photos/arctan/">my Flickr stream</a> and <a href="https://www.instagram.com/arcatan/">Instagram</a>.</li>
<li>Experimented with film photography: <a href="https://quanttype.net/posts/2015-11-04-dabbling-in-film-photography.html">I</a>, <a href="https://quanttype.net/posts/2015-12-08-color-film.html">II</a>.</li>
<li>Gave a talk titled <em>20th century functional programming</em> at <a href="http://www.meetup.com/Helsinki-Haskell-Users-Group/">Helsinki Haskell User Group</a>.</li>
<li>Posted 39 times on quanttype. My most popular post was <a href="https://quanttype.net/posts/2015-12-02-python-is-not-good-enough.html"><em>Python is not good enough</em></a>.</li>
</ul>
<h2 id="travel">Travel</h2>
<ul>
<li>Again celebrated <em>volbripäev</em> with <a href="http://www.raimla.ee/">Raimla</a> in Tartu.</li>
<li>Spent a week in <a href="https://quanttype.net/posts/2015-08-18-copenhagen.html">Copenhagen</a>.</li>
</ul>
<h2 id="best-of">Best of</h2>
<p>Here are some things that impressed me in 2015:</p>
<ul>
<li>Best fiction book: <em>Memory of Water</em> by <em>Emmi Itäranta</em>.</li>
<li>Best non-fiction book: <a href="http://machine.supply/books/arcatan/111"><em>Between the World and Me</em></a> by <em>Ta-Nehisi
Coates</em>.</li>
<li>Best movie: <em>The Man Without a Past</em>, directed by <em>Aki Kaurismäki</em>.</li>
<li>Best album: <a href="https://open.spotify.com/album/5YhPKINsoiSKMB2PXyxXPM"><em>Bullhorn</em></a> by <em>Verneri Pohjola</em>.</li>
<li>Best coffee shop: <a href="https://www.facebook.com/kahvikomppania/">Helsingin Kahvikomppania</a>. Consistently the best
cappuccino in town!</li>
</ul>
<h2 id="how-did-my-plans-for-2015-go">How did my plans for 2015 go?</h2>
<p>I did have a two-week summer holiday, I saw a wild hedgehog, and I finished
reading <em>Infinite Jest</em>. As said, I did not graduate as a BSc. Oh well.</p>
<h2 id="whats-next">What’s next?</h2>
<p>I’m joining <a href="http://www.metosin.fi">Metosin</a> as a part-timer and continue my
studies. It’s high time that I graduate as a bachelor of science. I hope to give
at least one talk at a tech meetup. Otherwise my plans are open.</p>
Math is hard, let's go blogging
https://quanttype.net/p/math-is-hard/
Wed, 23 Dec 2015 00:00:00 +0000https://quanttype.net/p/math-is-hard/<p>I had a blogpost prepared about probabilities, but then I realized that I had
messed up the mathematics. Because I have a
<a href="https://www.beeminder.com/">Beeminder</a> goal about blogging weekly, I have to
post something, so here’s a video of 2000 ducks. They flow and that’s awesome.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/gE2OjvyJmjE" frameborder="0" allowfullscreen></iframe>
Code review requires trust
https://quanttype.net/p/code-review-requires-trust/
Wed, 16 Dec 2015 00:00:00 +0000https://quanttype.net/p/code-review-requires-trust/<p>Why do we review code? Here are some of the reasons:</p>
<ul>
<li>To find bugs and fix problems in code before it’s deployed.</li>
<li>To get and give feedback on the system architecture.</li>
<li>To mentor and train developers.</li>
<li>To be aware of how some part of the system works.</li>
</ul>
<p>Clearly a big part of code review is giving feedback on code. This often
includes pointing out mistakes and areas of improvement. From experience I know
that receiving this kind of feedback sometimes hurts. It can hurt even when
given by a person with the best intentions.</p>
<p>It’s easy to end up defending yourself to avoid feeling hurt. However, if you
refuse to admit your mistakes, you won’t learn anything. This is why there must
exist a certain level of trust between the person giving the feedback and the
person receiving it. The person receiving the feedback must feel safe to admit
their mistakes.</p>
<p>This is especially important when your team includes juniors, who tend to feel
more insecure about their skills. Then again, if the senior people in your team
do not feel safe to admit mistakes, you’re in for some serious trouble, since
their mistakes are likely to have the biggest impact.</p>
<p><em>See also my <a href="https://quanttype.net/posts/2015-09-07-commandments-for-code-review.html">commandments for code review</a>.</em></p>
Color film update
https://quanttype.net/p/color-film/
Tue, 08 Dec 2015 00:00:00 +0000https://quanttype.net/p/color-film/<p>I finally shot and got developed my first roll of color film. The colors are
amazing! I don’t really understand what makes them look so much better
than the colors in my (and many others’) digital pictures and what to do about
it, but trying to emulate the look could be a worthwhile exercise.</p>
<figure><a data-flickr-embed="true" href="https://www.flickr.com/photos/arctan/22893270804/in/dateposted-public/" title="Baana"><img src="https://farm6.staticflickr.com/5748/22893270804_c14026b312_z.jpg" width="640" height="428" alt="Baana"></a><script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"></script></figure>
<figure><a data-flickr-embed="true" href="https://www.flickr.com/photos/arctan/23167819989/in/dateposted-public/" title="Pohjoinen rautatiekatu"><img src="https://farm6.staticflickr.com/5674/23167819989_77d6a06e5d_z.jpg" width="640" height="430" alt="Pohjoinen rautatiekatu"></a><script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"></script></figure>
<p>In other news, I’ve enjoyed browsing the <a href="https://www.flickr.com/groups/helsinkionfilm/">helsinki on film</a> pool on Flickr.
It’s more interesting than most Helsinki photography on Flickr.</p>
Python is not good enough
https://quanttype.net/p/python-is-not-good-enough/
Wed, 02 Dec 2015 00:00:00 +0000https://quanttype.net/p/python-is-not-good-enough/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/python_hu_dd2aeb128a4fa1f2.webp" type="image/webp" />
<img src="https://quanttype.net/images/python.jpg" width="600" height="280.2" loading="lazy" />
<figcaption><a href="https://flic.kr/p/yPV3VM">Python by William Warby</a>. CC BY 2.0.</figcaption>
</picture>
</figure>
<p>For the last six months, I’ve been working on some networked Python components
for a robotics application. There’s a web app, some data recording, etc. I’m
increasingly feeling that Python is not good enough language and ecosystem for
my needs. Here’s my beef:</p>
<p><strong>Debugging and profiling live systems is hard.</strong> I’d like to profile a Python
process with tens of threads. Better yet, it should be done in production,
because the performance issues never show up in the testing environment. Or
maybe I just want to get an idea of what a weirdly-behaving process is up to.
With Clojure, I’d open VisualVM and poke around. With Python, well, there’s a
bunch of profilers, but none of them seem to produce much useful information.</p>
<p><strong>Deploying is awkward.</strong> I have a bunch of Python projects and I’d like to
deploy them on a server. In Clojure world, I could build an uberjar, send that
to a server and run it. In Python world, self-contained builds do not exist. I
could build a wheel, or clone a git repo, but it won’t include the dependencies.
Of course,
with Docker you can make a self-contained build out of anything.</p>
<p><strong>Standard library.</strong> Python is “batteries included”, but
the batteries leak acid. I concur. Even
<a href="https://mail.python.org/pipermail/python-dev/2012-March/117529.html">Guido van Rossum admits it</a>.
There are sharp corners everywhere and the development speed is glacial. And do
you know if the standard library modules you’re using are thread-safe?</p>
<p><strong>Poor support for functional programming.</strong> Python’s lack of persistent data
structures and proper syntactic support for anonymous functions mean that
functional programming is cumbersome. I’ve fixed so many list and dict mutation
bugs that simply wouldn’t have happened in Clojure, because the standard data
types are immutable. Come on Python, even Java has full-power lambda
expressions!</p>
<p><strong>Poor concurrency support.</strong> Python’s concurrency tools are quite
traditional (native threads, forking, locking) and the performance has not been
great. Combined with the lack of persistent data structures, writing robust
concurrent programs is hard when compared to e.g. Clojure or Haskell. “You
shouldn’t write concurrent programs in Python” is not an acceptable answer. I
hear the things might be better with Python 3 and asyncio, though.</p>
<p><strong>Limited language extension possibilities.</strong> Higher-order functions are awkward
and there are no macros, so it’s hard to have your own control structures. I
realize that it’s part of the Zen of Python to not be able to extend the
language, but the Zen of me begs to differ. You seldom need this power, but it’s
great to have when you do.</p>
<p><strong>Awkward tooling.</strong> This is a personal preference, but iPython, pip and
virtualenv never seem to work quite as smoothly as Leiningen and Clojure REPLs,
or npm and the browser REPLs.</p>
<p>I don’t think that Python is a bad language or a bad ecosystem, per se. It’s
just that other languages like Clojure, JavaScript, Go, and Haskell have
made great strides forward in the last few years while Python has been falling
behind. For some niches like scientific computing and machine learning, Python
still is great: there are good libraries and an active community. For my niche
of writing network servers, it’s not where the action is anymore.</p>
<p>To end on a positive note, here are some things I like about Python:</p>
<p><strong>Docstrings.</strong> More languages should have docstrings, or otherwise a standard
convention for writing function and module documentation.</p>
<p><strong>Keyword arguments with default values.</strong> You see awkward implementations of
keyword arguments everywhere in JavaScript and Clojure code, because they’re
great!</p>
<p><strong>There are some great libraries.</strong> Lately I’ve enjoyed using
<a href="http://python-requests.org">Requests</a> and
<a href="http://werkzeug.pocoo.org">Werkzeug</a>. <a href="http://www.numpy.org">numpy</a> has served
me well whenever I have needed it. I’m impressed by
<a href="https://github.com/DRMacIver/hypothesis">Hypothesis</a>, although I haven’t been
able to use it much.</p>
<p><strong>It’s good for small command-line utilities.</strong> Fast startup, no compilation,
and argparse means it’s easy to whip up a quick CLI tool.</p>
Printers now work
https://quanttype.net/p/printers-have-started-working/
Wed, 25 Nov 2015 00:00:00 +0000https://quanttype.net/p/printers-have-started-working/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/printer_hu_210cd45b44ce5e0f.webp" type="image/webp" />
<img src="https://quanttype.net/images/printer.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>Our home printer, Epson XP-325. It works!</figcaption>
</picture>
</figure>
<p>I don’t know if you have noticed, but printers nowadays work. In the last few
years, I’ve set up and used a bunch of printers and it has been very much a
plug-and-play experience. Just connect the printer to the wireless network and
you’re ready to print. I use OS X, but I hear it’s the same for my Linux-using
friends. I’ve also successfully printed from my phone. Heck, I even scan
wirelessly from my laptop!</p>
<p>Ink is still atrociously expensive and runs out immediatel, and printers are
getting less and less important as people move away paper. Still, it’s cool that
we’ve finally made the printers work. They used to be such a pain.</p>
<p>For other things that have started working in this millenium, see Dan Luu’s
article
<a href="http://danluu.com/butler-lampson-1999/">What’s Worked in Computer Science</a>.</p>
Spinning while sleeping
https://quanttype.net/p/spinning-while-sleeping/
Wed, 18 Nov 2015 00:00:00 +0000https://quanttype.net/p/spinning-while-sleeping/
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/sleep.gif" width="600" height="179.0625" loading="lazy" />
</picture>
</figure>
<p>I have some long-running scripts that sleep while waiting for something to
happen. It’d be nice to look at the terminal and know whether the process is
working or sleeping. To that end, I replaced some of the sleep(1) invocations
with <a href="https://gist.github.com/miikka/ef51606feb0ed0373861">spinner.py</a>, which shows a simple spinner while sleeping.</p>
<p>Now, David R. MacIver <a href="https://twitter.com/DRMacIver/status/667041874247557120">pointed out</a> that I of course should’ve enhanced
sleep(1) with a <code>LD_PRELOAD</code> hack. <code>LD_PRELOAD</code> is an environmental variable
that tells the Linux dynamic loader to look at given shared objects before
loading anything else. This allows, among other things, overwriting calls to the
C standard library.</p>
<p>I’ve never used <code>LD_PRELOAD</code> before, but it turned out to be simple! Create a
shared library with a function with the name and signature of the function you
want to overwrite, point <code>LD_PRELOAD</code> to it and you’re done.</p>
<p>A quick search for <em>gnu sleep source</em> tells me that
<a href="https://fossies.org/dox/glibc-2.22/sysdeps_2unix_2sysv_2linux_2sleep_8c_source.html">sleep(1) uses nanosleep(2)</a>, so that’s the function I rewrote:</p>
<script src="https://gist.github.com/miikka/bb2c26034074ebec156f.js"></script>
<p>Making this work on OS X is left as an exercise for the reader.</p>
Joylent mini-review
https://quanttype.net/p/joylent-mini-review/
Wed, 11 Nov 2015 00:00:00 +0000https://quanttype.net/p/joylent-mini-review/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joylent_hu_8b80e1586f8048f7.webp" type="image/webp" />
<img src="https://quanttype.net/images/joylent.jpg" width="600" height="337.8" alt="A glass of chocolate-flavored Joylent." loading="lazy" />
</picture>
</figure>
<p>A year ago <a href="https://quanttype.net/posts/2014-12-17-ambronite-mini-review.html">I reviewed Ambronite</a>. Now I’m back with
<a href="http://www.joylent.eu">Joylent</a>. Like Ambronite and Soylent, it’s a nutritional
powder. A friend gave me a bag, so let me offer you a mini-review.</p>
<p>I got the chocolate-flavored Joylent, but it’s also available in strawberry,
vanilla and banana flavor. Friends in the know tell me that chocolate is the
best one, though. What it actually tastes like is artifically-sweet bland choco
milk.</p>
<p>Like Ambronite, it has the astringent taste of banana peels (i.e. it contains
tannins). Ambronite’s astringency might come from <a href="https://twitter.com/Samuheino/status/660389726529019904">nutritional yeast</a>,
but about Joylent I don’t know. I asked around, but almost nobody complained
about astringency in Joylent. I’m starting to suspect that I have some genetic
predisposition for tasting bitterness!</p>
<p>How about the mouthfeel, then? Joylent is powdery and mixes very well with
water. Unlike Ambronite, it does not have almost any texture. It’s like drinking
thick milk. This gives it a less wholesome feel. Nevertheless I felt satiated
after drinking a portion.</p>
<p>To be honest, I liked Ambronite more. I’d go with the exquisite (and bitter)
sawdust instead of the bland (and bitter) choco milk.</p>
Dabbling in film photography
https://quanttype.net/p/dabbling-in-film-photography/
Wed, 04 Nov 2015 00:00:00 +0000https://quanttype.net/p/dabbling-in-film-photography/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/nojatuoli_hu_c1ba4da3e15af955.webp" type="image/webp" />
<img src="https://quanttype.net/images/nojatuoli.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>A broken armchair in Kallio. One of my first film shots. (<a href="https://flic.kr/p/z2HZhd">Flickr</a>)</figcaption>
</picture>
</figure>
<p>I became interested in film photography when a friend showed me how
take photos with a beer can and photo paper.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> I figured out it’d
cool to take some photos with a real camera, too. Soon I managed to
borrow a film SLR and I’ve now shot two rolls of B&W film. Here are my
initial thoughts.</p>
<h2 id="gear-talk">Gear talk</h2>
<p>The camera I borrowed a Minolta 7000 AF with a AF 28-135 mm 1:4-4.5
lens. It’s a 35 mm SLR camera that came out in 1985. Apparently it’s
the world’s first SLR camera with autofocus.</p>
<p><a href="http://www.kenrockwell.com/minolta/maxxum/7000.htm">Ken Rockwell describes it</a> as a straightforward camera that
is simple and enjoyable to use. I concur. It also has suprisingly good
ergonomics for my big hands. It has buttons for selecting aperture and
shutter time. I’d slightly prefer dials, but at least the buttons are
easy to reach. The focus ring is unconventionally in the rear of the
lens, but it’s also easy to reach there.</p>
<p>Even though the camera has autofocus, I’ve ended up focusing manually.
The AF is slow and noisy and not that good. Some of my pictures have
been mis-focused, but I doubt AF would’ve done any better job.</p>
<h2 id="film-talk">Film talk</h2>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/nuuksio_hu_8f15df087d5499d1.webp" type="image/webp" />
<img src="https://quanttype.net/images/nuuksio.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>The colors of autumn are so beautiful I shot them in black-and-white. (<a href="https://flic.kr/p/zhtM3K">Flickr</a>)</figcaption>
</picture>
</figure>
<p>The film I first tried was Agfaphoto APX 400. I didn’t know what I
want, so I went to the local photography store and asked for a
black-and-white ISO400 film. APX 400 is what the cheapest option
available.</p>
<p>Since it’s the only film I’ve used so far, it’s hard to make
comparisons. I’m happy how my urban photos have turned out, but humans
in my photos look weird. Right now I’m trying out color film, but
after that I’ll experiment with some other B&W films.</p>
<p>So far I’ve let a local photography shop develop and print my film.
I’d like to try developing myself to get the full film experience. I
was planning to go to a darkroom course organized by the university
photography club, but I messed up some dates and didn’t even manage to
sign up befor the courses were over.</p>
<h2 id="so-what-have-i-learned">So what have I learned?</h2>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/nuuksio2_hu_52d298ce54b1dac9.webp" type="image/webp" />
<img src="https://quanttype.net/images/nuuksio2.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>I’m starting to like black-and-white nature photography… (<a href="https://flic.kr/p/zhtM3K">Flickr</a>)</figcaption>
</picture>
</figure>
<p><em>Prints looks stunning</em>. Really. I had totally forgotten how good
printed photos can look. The depth and the contrast are great. These
aren’t any special prints, either, just the ordinary 10x15 prints from
the local shop from my so-so first roll of film. I believe it’s the
magic of the printer and not the magic of the film - I have to get
some digital photos printed.</p>
<p><em>Photography has become so much easier</em>. With digital cameras, you get
immediate feedback. You have to develop the film before you even know
if you had suitable exposure settings. Film also costs money. Digital
cameras give you free infinite retries, and you do not need to know in
advance if you’re going to shoot B&W or color, and what ISO you need.</p>
<p>Another thing is that it’s so easy to edit digital photos. My first
prints taught me that I can’t shoot straight. I’ve always relied on
straightening the photos in post-processing.</p>
<p>The cumbersomeness of film photography does make you think more about
the photos you’re shooting. It’s a good exercise, but I’m glad I have
my digital camera.</p>
<p><em>My scanner sucks</em>. What do you do with photos if you don’t post them
on social media? They must be scanned, then. Turns out my scanner isn’t
really up to the task. Oh well, I guess you can’t expect too much from
a 50 € printer/scanner combo.</p>
<h2 id="whats-next">What’s next?</h2>
<p>I’m going to shoot a couple of more rolls of film, both color and B&W.
I want to get some pictures of snow once the winter hits Helsinki.
Maybe I also find a way to develop the films myself.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>See e.g. <a href="http://www.pinholephotography.org/Beer%20Can%20construction.htm">these instructions</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Webpacking a project
https://quanttype.net/p/webpacking-a-project/
Wed, 28 Oct 2015 00:00:00 +0000https://quanttype.net/p/webpacking-a-project/<p>This week I used <a href="http://webpack.github.io">Webpack</a> at work and it was great. Let me tell you about it.</p>
<p>I took over an experimental JavaScript project with the aim of making it a
proper part of our software engineering process. This means cleaning up the
code, integrating it with our build system, ensuring that it has essential
documentation (“how to run it”), etc.</p>
<p>The code didn’t look too bad. It’s a browser-based tool with a single HTML file
and a couple of thousands lines of JavaScript split across a dozen files. There
was no build step - all the JavaScript files were included in HTML with
<code><script></code> tags, and the dependencies were checked into the repo. There were
some bad signs, too: there were no tests and the code was weirdly formatted.
Still, it was clean enough. And at least there was no sign of Angular!</p>
<p>As the first step, I ran all the code through
<a href="https://www.npmjs.com/package/js-beautify">js-beautify</a>. Did you know that
Spacemacs has a shortcut for running js-beautify? It’s <code>SPC m =</code>. This took care
of all the weird indentation.</p>
<p>The code was split into one object/“class” per file. It was almost modular
style, except that of course all the files share the same scope. There was a
main module that uses all the other modules, but also defines some global
variables that the other modules use.</p>
<p>I figured out that it’s a good idea to set up
<a href="http://jshint.com/about/">JSHint</a>. The initial run didn’t reveal any problems
beyond some missing semicolons, but I didn’t want to break anything. Since
JSHint does not know that the files share the same scope, I had to add all the
global variables to the globals array in <code>.jshintrc</code>.</p>
<p>The global variables containing the application state made me feel uneasy. Since
the code was almost modular already, I realized it would be easy to use
<a href="http://webpack.github.io">Webpack</a> to get real modules. Webpack (like
browserify) takes your code, wraps each file into a module and gives you
Node-style interface with <code>require()</code> and <code>module.exports</code>. Using modules would
make the global state explicit and Webpack would also make it easy to use ES6,
to have live-reloading code, and other good stuff.</p>
<p>What I did was to add an entrypoint that requires the main module and calls the
function that starts the application. Initially I only included the application
code in webpack and pulled in the dependencies in the old <code><script></code> way.</p>
<p>Then I removed the module names from JShint globals one-by-one. JShint gave me a
list of all the files that needed attention and I added the relevant imports and
exports there.</p>
<p>To deal with the global state variables, I added a new module called
<code>GlobalState</code> to contain them. If I have time to refactor this later, finding
all the places that use it is just one grep away.</p>
<p>Most of the third-party dependencies could be pulled from NPM. Create a
<code>package.json</code> with your dependencies, sadd <code>node_modules</code> to Webpack’s resolve
roots, run <code>npm install</code> and you’re good to go.</p>
<p>With the more obscure libraries, I decided to keep the vendored libraries in the
repository and use
<a href="http://webpack.github.io/docs/shimming-modules.html">shimming</a> to make them
work with Webpack. For example to use
<a href="https://github.com/RobotWebTools/roslibjs">roslibjs</a>, I added the following
loader:</p>
<pre><code>// roslibjs requires EventEmitter2 and defines ROSLIB
{
test: /roslib\.js$/,
loaders: [
'imports?EventEmitter2=eventemitter2',
'exports?ROSLIB'
]
}
</code></pre>
<p>Then I could do <code>var ROSLIB = require('roslib.js')</code> anywhere I wanted.</p>
<p>When integrating Webpack with rest of our build system, I wanted to pass it the
resolve roots as a command-line flag. Webpack does not have such a flag
out-of-the-box. This is not a problem since <code>webpack.config.js</code> is an ordinary
Node module and you can use e.g. <a href="https://www.npmjs.com/package/yargs">yargs</a> to
parse the flags:</p>
<pre><code>var argv = require(’yargs’).argv;
module.exports = {
resolve: {
root: argv.resolveRoot
}
};
// now do webpack —resolve-root=foo —resolve-root=bar
</code></pre>
<p>I like the declarative configuration of Webpack. Basically you say “here is my
code, here are my deps, please compile” and it does what you want. To get ES6,
just add <code>babel-loader</code> to the loaders. To get live-reloading, just run the dev
server with <code>--hot --inline</code>. This is so much better than complex gulpfiles. If only the documentation was less confusing!</p>
Exercising is like eating
https://quanttype.net/p/exercising-is-like-eating/
Wed, 21 Oct 2015 00:00:00 +0000https://quanttype.net/p/exercising-is-like-eating/<p>I used to think that you should have fun while exercising. I had trouble
building an exercise habit since, let’s face it, exercise isn’t always fun.
Sure, it’s often great, but sometimes it’s just boring and tedious.</p>
<p>Recently there has been a shift in my thinking. Exercising is like eating: you
have to do it whether you like it or not. Realizing this has made it so much
easier for me to go jogging. If running does not feel so good this time, it’s
not a huge disappointment, it’s just life. Aftewards I’m pretty much always
happy that I did it.</p>
Moral obligations of ad blocking
https://quanttype.net/p/moral-obligations-of-ad-blocking/
Fri, 09 Oct 2015 00:00:00 +0000https://quanttype.net/p/moral-obligations-of-ad-blocking/<p>With iOS 9, <a href="http://thenextweb.com/apple/2015/08/24/ios-9-content-blocking-will-transform-the-mobile-web-ive-tried-it/">Apple enabled ad blocking in Mobile Safari</a>. This change
received a lot of attention. Users were happy, because web ads and trackers
significantly degrade the user experience and intrude privacy. However,
publishers were quick to announce that their publications are doomed, since they
will be unable to make any money when everybody blocks ads.</p>
<p>A central question of the debate is this: <em>are users morally obliged to not
block ads?</em></p>
<p>I look this question from the perspective of software compatibility. When you
publish a website, you’re essentially shipping a piece of software of your
readers around the world. Some of them are bound to be using setups that are not
fully compatible with your site. Maybe they’re running an old browser that
doesn’t support the latest HTML5 features, or maybe they’re running an ad
blocker. Do they have a moral obligation to maintain 100% compatibility with
your website?</p>
<p>A couple of years ago, Flash ads were very popular. A simple way to block a big
chunk of the most annoying ads was to uninstall Flash. Do your users have a
moral obligation to have Flash installed?</p>
<p>A web site is a kind of distributed system: typically the user’s computer will
have to gather the pieces of code and content from a bunch of different servers.
<a href="https://en.wikipedia.org/wiki/Network_partition">Network partitions</a> are a common failure mode of distributed systems.</p>
<p>For example, I have this one computer that does not run ad-blocking software per
se, but that is unable to connect certain well-known hosts that serve tracking
code. It is essentially partitioned off of certain parts of the Internet. Do
your users have a moral obligation to ensure that there are no network
partitions in your distributed systems?</p>
<hr>
<p><em>How to make money with Internet publishing</em> is a hard question. I do not have
an answer, but I doubt that limiting the users’ ability to be picky about the
software they run on their computers is the way.</p>
Commandments for code review
https://quanttype.net/p/commandments-for-code-review/
Mon, 07 Sep 2015 00:00:00 +0000https://quanttype.net/p/commandments-for-code-review/<p>Code review is the practice of soliciting and giving feedback on code. At work, reviewing is an integral part of our software engineering process. Code won’t be merged to the master before at least one person has reviewed it — the only exception is experimental research code. This means that over the past two years, I’ve reviewed a lot of code.</p>
<p>How do you review code, then? I’ve distilled my experience into a short list of ”commandments”:</p>
<ol>
<li>Review the code, not the people.</li>
<li>Review what computers can’t review.</li>
<li>Give constructive feedback.</li>
<li>Praise good work.</li>
<li>Everybody can and should review everybody’s code.</li>
<li>Stay humble.</li>
</ol>
Some questions I can't answer
https://quanttype.net/p/some-questions-i-cant-answer/
Sun, 06 Sep 2015 00:00:00 +0000https://quanttype.net/p/some-questions-i-cant-answer/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/concrete_hu_7d8c4d79b2689702.webp" type="image/webp" />
<img src="https://quanttype.net/images/concrete.jpg" width="600" height="337.2" loading="lazy" />
<figcaption>This is a picture of a concrete building in a post about building concrete knowledge.</figcaption>
</picture>
</figure>
<p>I feel like I know quite a bit about programming, but a lot of that knowledge is tacit. I know intuitively how various bits fit together, but it’s hard to put that knowledge into words. One of the reasons why I write this blog is to practice making my knowledge explicit.</p>
<p>Today I was thinking of something else: how to make my lack of knowledge explicit? If you know what you don’t know, that’s fine, you can work on it. If you don’t know it, you might be in trouble. Who knows? Not you, that’s for sure.</p>
<p>To get started, I decided to map some of my known unknowns. I wrote down some questions about software engineering that I can’t answer. I recommend this exercise: it is surprisingly difficult to condense the unknowns into concrete questions. Here is my list:</p>
<p><strong>How do you manage dependency updates for JavaScript projects?</strong> JavaScript projects tend to have a lot of fast-moving dependencies, and the common practice seems to be to use bleeding-edge versions of them. How do you ensure that the updates do not break your software?</p>
<p><strong>Are projects a good way to organize software engineering work?</strong> I mean project as in subcontracting project, not as in open source project. The question probably gives away the fact that I think the answer might be ”no”. Projects by definition have end date, but to me it seems that software that is used requires continuous work. What are alternatives to projects, though?</p>
<p><strong>What does a well-working software engineering process look like?</strong> I’m thinking of something like agile-as-in-the-manifesto, but what are the essential parts?</p>
<p><strong>What makes a senior programmer?</strong> I mean senior-as-recognized-by-peers, not senior-as-in-title. What are the essential skills and skill areas? Seniority is a continuum, but are there natural checkpoints on that continuum?</p>
<p><strong>What were the most significant new discoveries in software engineering and computer science in the 00s?</strong> What will the years 2000-2009 be remembered for? My knowledge on this is so weak that I don’t even know what to suggest. Maybe the advances to deep learning could be on the list?</p>
<hr>
<p>These are the questions on my mind tonight. They are <em>my</em> unknowns: obviously there are many people out there who could give well-reasoned answers to these questions. It also shows what I’ve been thinking about lately. The list will look different next month, or next year.</p>
<p>So, what does your list look like?</p>
Everyday carry (August 2015)
https://quanttype.net/p/everyday-carry/
Sun, 30 Aug 2015 00:00:00 +0000https://quanttype.net/p/everyday-carry/<p><em>Everyday carry</em> (EDC) means the collection of items you lug around with you
every day. It’s also the one of the most popular genres of <em>manly</em> lifestyle
blogging. A typical EDC post contains a picture of knolled contents of the
author’s EDC, along with a list of the items with their brand names and
optionally a description why each item was chosen. One of the biggest EDC blogs
is <a href="http://everydaycarry.com">everydaycarry.com</a>.</p>
<p>In theory, EDC is about being to prepared to the situations you might end up in.
That’s why you’ll see lots of multitools, knives and other tools in the EDC
postings. If you browse everdaycarry.com, it quickly becomes obvious that the
posts are also about style, fashion and building your identity.</p>
<p>I find it fascinating to browse EDC posts. What even is a tactical pen? Do all
those software architects really use all those knives for something? What do
people write or draw into all those Field Notes notebooks? Where are all the
charging cables?</p>
<hr>
<p>Let’s have my contribution to the genre, then.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/edc_hu_daa9d5f03473036e.webp" type="image/webp" />
<img src="https://quanttype.net/images/edc.jpg" width="600" height="400.2" loading="lazy" />
</picture>
</figure>
<p>I almost always have my backpack with me and typically carry around a lot of
stuff. In this picture I’ve included only the items whose absence nags me.</p>
<p><strong>Smartphone</strong>. It’s a communication device, but it’s also a book, a camera, a
map, a notebook and a bus timetable.</p>
<p><strong>Smartphone charger</strong>. Unfortunately, the battery of an actively used
smartphone runs out quickly. Carrying a smartphone without battery is almost
worse than just leaving the smartphone home. This means I have to be always
ready to charge it when the need arises.</p>
<p>There’s actually a recent study by Ferreira, McGregor and Lampinen about the
impact of smartphone batteries on everyday life! The article is called
<a href="https://drive.google.com/file/d/0B5j6JnknXVcid3ZxM0lFOGM2M0E/view">Caring for Batteries</a>. Here’s a conclusion they draw:</p>
<blockquote>
<p>[W]e see how battery work impacts our lives in various ways. Also, we see how
it is not just about particular moments when batteries go flat, but rather a
matter of constant strategizing and anticipating of when and where one will be
able to charge, navigating between a complex and varying infrastructure that
we learn, build, and maintain. We take these tasks upon ourselves despite the
stress they bring into our lives (as seen in the richness of the
emotional-laden vocabulary used by participants); we rarely reflect back on
these tasks. Perhaps there is a feeling that little can be done and we are
unable to consider battery care as something optional.</p>
</blockquote>
<p><strong>Notebook, pen and pencil</strong>. I like to <a href="https://quanttype.net/posts/2015-04-21-take-pen-and-paper-notes.html">take lecture notes on paper</a>.
Sometimes I <em>think on paper</em>: if I can’t figure something out, I write what I
know about in on paper or draw a picture. It usually helps.</p>
<p>Writing in the notebook feels too permanent, so I actually write on throw-away
pieces of found paper, yet I carry around the notebook. I should work on this.</p>
<p><strong>Watch</strong>. For years, I didn’t use watch, instead relying on the smartphone as a
clock. I didn’t like the fact that my clock ran out of battery so often, so this
spring I bought a watch. It hasn’t yet run out of battery. Success!</p>
<p><strong>Key-sized multitool</strong>. A.k.a. the only multitool I ever actually use. Compared
to bigger multitools, this one sucks. However, it totally beats using your keys
as improvised cutting tools! I use this to open packages and to cut strings
almost daily. It’s made by Semptec, but it seems to be identical to
<a href="http://www.swisstechtools.com/proddetail.aspx?pid=5">Swiss+Tech Utili-Key</a>.</p>
<hr>
<p>For further reading, check out Venkatesh Rao’s recent post
<a href="http://www.ribbonfarm.com/2015/06/12/the-things-you-carry/">The Things You Carry</a>.</p>
Copenhagen highlights
https://quanttype.net/p/copenhagen/
Tue, 18 Aug 2015 00:00:00 +0000https://quanttype.net/p/copenhagen/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/bike_hu_2822a3bceab2c608.webp" type="image/webp" />
<img src="https://quanttype.net/images/bike.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>A cargo bike. Copenhagen is full of them. (<a href="https://flic.kr/p/xqBFqN">Flickr</a>)</figcaption>
</picture>
</figure>
<p>I’m on vacation and I spent the last week in Copenhagen, Denmark. It was a
tourist trip - I even saw the <a href="https://en.wikipedia.org/wiki/The_Little_Mermaid_(statue)">Little Mermaid</a>! Here are my highlights.</p>
<p><strong>Everything works.</strong> Copenhagen was very easy to visit. Everybody spoke perfect
English, all information was easily available in English and on the Internet.
Everything was clean and everything worked. My only complaint is that not every
bathroom has a single-handle mixer tap. Come on, Danes, you even have
<a href="http://www.thekitchn.com/colorful-kitchen-faucets-vola-by-arne-jacobsen-kitchen-inspiration-168063">an iconic design tap</a>, there’s no excuse.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/elmegade_hu_2b2b8cd41fbbd4fa.webp" type="image/webp" />
<img src="https://quanttype.net/images/elmegade.jpg" width="600" height="337.8" loading="lazy" />
<figcaption>A typical Copenhagen street, full of bikes. How can there be some many bikes?</figcaption>
</picture>
</figure>
<p><strong>Bike infrastructure.</strong> Cyclists in Helsinki sometimes lament how much worse
the cycling infrastructure is compared to Copenhagen. I’ve thought that it can’t
be that much better, but yes it can. Bike paths are everywhere, they’re full of
cyclists and devoid of parked cars. Wow.</p>
<p><strong>Museums.</strong> I liked <a href="http://www.glyptoteket.dk">Glyptoteket</a>. They had an interesting exhibition
about <a href="http://www.glyptoteket.com/whats-on/calendar/man-ray-human-equations">Man Ray and his Shakespearean Equations</a>, their collection of
French masters was great and the winter garden is beautiful. We also visited
<a href="http://en.louisiana.dk">Louisiana</a>, the museum of modern art. The exhibitions were high
quality, but I wasn’t so interested in them. Still, I recommend visting Lousiana
just to see the modernist buildings and the beautiful site.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/groed_hu_81dd29849db442a8.webp" type="image/webp" />
<img src="https://quanttype.net/images/groed.jpg" width="600" height="600" loading="lazy" />
<figcaption>A bowl of oat porridge with fresh apple and caramel sauce. Yum.</figcaption>
</picture>
</figure>
<p><strong>Food.</strong> We visited some nice places, but the best food experience was at
<a href="http://groed.com/">GRØD</a>. This is a bit surprising since it’s a fast-food joint serving
porridge. They served some of the best oat porridge I’ve ever had and I’ve had a
lot of porridge in my life. Also, what’s not to like about <a href="http://newnordicfood.org/about-nnf-ii/new-nordic-kitchen-manifesto/">new Nordic</a> fast
food?</p>
<p><strong>Coffee.</strong> <a href="http://coffeecollective.dk">The Coffee Collective</a> is an obvious place to visit for coffee,
but another great coffee shop I visited was <a href="http://www.forlorenespresso.dk">Forloren Espresso</a>
(<em>false espresso?</em>). I got an excellent cup of Kaiguri AA that was meticulously
hand-brewed.</p>
<p>You can find both the The Coffee Collective and GRØD in Torvhallerne (<em>market
halls?</em>) near Nørreport station. However, I recommend visiting their other
locations. Torvhallerne are very busy and you’ll get better service in the
calmer places. Instead, go to Jægersborggade in Nørrebro. The street is full of
small design shops and restaurants. I imagine Vaasankatu in Helsinki will look
like Jægersborggade in ten years.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/norrebro_hu_2bb8c3680c648847.webp" type="image/webp" />
<img src="https://quanttype.net/images/norrebro.jpg" width="600" height="400.2" loading="lazy" />
<figcaption>Street corner with graffiti in Nørrebro.</figcaption>
</picture>
</figure>
<p>All my friends in Helsinki seem to be going to Copenhagen right now. I can see
the appeal: even though Copenhagen isn’t that much bigger than Helsinki, it
feels much bigger, yet it has the Nordic flavor. Unfortunately it has the Nordic
price level, too…</p>
<p>Personally, I didn’t fall in love with Copenhagen. It’s a vibrant city, but it’s
too busy and noisy for me. I’m glad that I went there, but I don’t think I will
be back too soon. Next time I travel, I’m going to go see some nature.</p>
Shell pro-tip: create weekly working directories
https://quanttype.net/p/mess/
Mon, 10 Aug 2015 00:00:00 +0000https://quanttype.net/p/mess/<p>Is your home directory filled with temporary files, directories and other kinds
of experiments? Mine was until I learned this tip from
<a href="http://chneukirchen.org/blog/archive/2006/01/keeping-your-home-clean-with-mess.html">Leah Neukirchen’s blog</a> almost ten years ago: every week I create a
new directory to use.</p>
<p>The path is <code>~/mess/YEAR-WEEK</code>, so this weeks path is <code>~/mess/2015-33</code>. I have a
small shell function called <code>mess</code> that cds to the current weekly directory,
creating it if it does not exist. It also points the symlink <code>~/mess/current</code> to
the current weekly directory. I’ve also added <code>mess</code> to zsh’s directory hash
table, so <code>~mess</code> points to <code>~/mess/current</code>.</p>
<p>Here’s the code:</p>
<pre><code>hash -d mess=~/mess/current
function mess() {
local MESSDIR=~/mess/`date +%G-%V`
if [ ! -e $MESSDIR ]; then
mkdir -p $MESSDIR
ln -snf $MESSDIR ~/mess/current
fi
cd $MESSDIR
}
</code></pre>
Wash your smelly travel towel with vinegar
https://quanttype.net/p/wash-your-smelly-towel-with-vinegar/
Sun, 02 Aug 2015 00:00:00 +0000https://quanttype.net/p/wash-your-smelly-towel-with-vinegar/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/etikka_hu_2eab4b726d0a05a4.webp" type="image/webp" />
<img src="https://quanttype.net/images/etikka.jpg" width="600" height="450" alt="Travel towel and a nice bottle of vinegar." loading="lazy" />
</picture>
</figure>
<p>Do you have a smelly travel towel? Soak it in vinegar to clean it up.</p>
<p>I have one of those microfiber travel towels. They’re great: they take very
little space, dry up quickly, and after breaking in, they’re comfy, too. There’s
just one problem with them: if you forget to let them dry, they will stink (like
all towels) and it’s hard to get rid of the smell (unlike cotton towels).</p>
<p>This happened to me. I forgot my towel in the bag overnight and next morning it
smelled like vomit. The smell didn’t go away even after washing the towel
several times. In fact, the smell seemed to become even worse. I was ready to
throw the towel away, but then a friend suggested using vinegar. I tried it and
it worked great! The towel is non-stinky and usable again.</p>
<p>Here’s what I did:</p>
<ol>
<li>Submerge the towel into a solution of equal parts of vinegar and water. Leave
it there for 24 hours.</li>
<li>Rinse the towel.</li>
<li>Wash it the usual way (machine wash warm).</li>
</ol>
<p>I believe that using vinegar is a well-known method among the more experienced
laundry-washers, yet my googling didn’t bring it up. Hopefully writing this down
will help the next person with the same problem.</p>
FRP and self-adjusting computation
https://quanttype.net/p/frp-and-sac/
Sat, 25 Jul 2015 00:00:00 +0000https://quanttype.net/p/frp-and-sac/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/stream_hu_e4fe0cb5c281a299.webp" type="image/webp" />
<img src="https://quanttype.net/images/stream.jpg" width="600" height="337.8" loading="lazy" />
<figcaption>Modern FRP is all about event streams.</figcaption>
</picture>
</figure>
<p>If you <a href="https://twitter.com/arcatan">follow me on Twitter</a> attentively, you know that a while ago I
realized that <em>functional reactive programming</em> (FRP) and <em>self-adjusting
computation</em> (SAC) are related, but I didn’t know what the difference is. I have
now figured it out.</p>
<p>Functional reactive programming is programming with values that change over
time. For example, if you want to move a sprite on the screen, the position of
the sprite changes as a function of time.</p>
<p>Umut Acar, whose PhD thesis was the original work on self-adjusting computation,
<a href="http://www.umut-acar.org/self-adjusting-computation">describes SAC</a> thus:</p>
<blockquote>
<p>Self-adjusting computation refers to a model of computing where computations
can automatically respond to changes in their data. […] Perhaps the most
important idea in self-adjusting computation is the treatment of computation
as a “first-class” mathematical and computational object that can be
remembered, re-used, and adapted to changes in its environment.</p>
</blockquote>
<p>So, both FRP and SAC are about computations with changing inputs. What’s the
difference?</p>
<p>The best explanation I’ve found comes from the blog post
<a href="https://blogs.janestreet.com/breaking-down-frp/">Breaking down FRP</a> by Yaron Minsky: FRP is history-sensitive and SAC is
not. In FRP, the values that change over time, called <em>behaviors</em>, can be
defined in terms of the history of their inputs. In SAC, only the current state
can be used.</p>
<p>It can be helpful to thinking about the context where FRP and SAC were
developed. FRP’s roots are in <a href="http://conal.net/papers/ActiveVRML/">animations in virtual reality</a>. SAC
was developed for efficiently implementing algorithms where part of the input
changes and you do not want to recalculate everything. Also, there’s an answer
on Theoretical Computer Science StackExchange that explains
<a href="http://cstheory.stackexchange.com/a/9308">when to use FRP, SAC and partial evaluation</a>.</p>
Running is great for lazy people
https://quanttype.net/p/running-is-great-for-lazy-people/
Mon, 20 Jul 2015 00:00:00 +0000https://quanttype.net/p/running-is-great-for-lazy-people/<p>If you’re looking for an easy way to get some exercise, try running. It
definitely worked for me. Over the years, I’ve tried to build swimming, cycling
and gym habits, but they never lasted. In March I started running three times a
week and I’ve have ran almost every week ever since.</p>
<ul>
<li>You can run almost anywhere and at any time. I like swimming, but the pool
closes so early and besides first you have to go there. With running, you can
just step outside and you’re ready to go.</li>
<li>You do not need much gear. I like cycling, but sometimes my bicycle breaks and
fixing it is no fun. Sneakers do not suddenly stop working.</li>
<li>You can run alone. No need to arrange a group of people to be at the same
place at the same time.</li>
</ul>
<p>In other words, running is the perfect hobby for lazy opportunistic people! If
you get an urge to get some exercise, there’s no excuse for not following the
urge.</p>
<p>Admittedly, running itself is not that exciting. I’ve solved this by listening
to podcasts - <a href="http://beatsryetypes.com/">Beats, Rye & Types</a> and <a href="http://tyyppimuunnos.fi">Tyyppimuunnos</a> (in Finnish) are
some of my favorites. Sometimes the weather is bad, but so far I’ve always been
glad that I went out. We will see what happens in the winter.</p>
<p>They say that regular exercise is good for your health. The only change I’ve
noticed so far is that I now have a daily urge to get some exercise. Beware the
addiction.</p>
The Moat of Scrumbut
https://quanttype.net/p/the-moat-of-scrumbut/
Mon, 13 Jul 2015 00:00:00 +0000https://quanttype.net/p/the-moat-of-scrumbut/<p>Whenever there’s talk about agile software development, someone will tell you
about how their team was burned by agile methods, and someone will counter that
the team just didn’t do agile right.</p>
<p>Agile proponents promise sky-rocketing productivity, but if you do just a small
mistake, you end up doing worse than by just doing whatever you were doing
before. Here’s how it looks to me:</p>
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/moat-of-scrumbut.png" width="600" height="447.3815461346633" loading="lazy" />
<figcaption><a href="http://agileatlas.org/articles/item/fractional-scrum-or-scrum-but">Scrumbut</a> is where you almost use Scrum: “We use Scrum, but…”</figcaption>
</picture>
</figure>
<p>What’s up with the moat of scrumbut? <em>Is agile really this fragile?</em></p>
<p>You don’t want to use a fragile development method. There will be situations
where you can’t fully follow the process. Even things like vacation season can
cause problems with following the rules to the letter. This should be a small
speed bump, not a catastrophe.</p>
<p>So, what’s up? I don’t know - I do not have enough experience to say. I suspect
that some of this is caused by what Martin Fowler calls <a href="http://martinfowler.com/bliki/FlaccidScrum.html">Flaccid
Scrum</a>. In Flaccid Scrum, you pick up the agile management practices,
but none of the agile technical practices. Scrum does not mandate any specific
technical practices, after all. Neglecting the technical process will get you in
trouble.</p>
<p>If everybody does the same big mistake, that does not yet make the method
fragile. It does suggest that there are problems in how the method is taught,
though.</p>
Speech as art
https://quanttype.net/p/speech-as-art/
Mon, 06 Jul 2015 00:00:00 +0000https://quanttype.net/p/speech-as-art/<p><em>Kimmo Modig</em> is this artist whose work tends to center around
speaking and performance. I’ve been following his work for a while and
I find it weirdly brilliant. You can find many of his works online, so
let’s have a look.</p>
<p>First, <em>Express Yourself</em> (2012). It’s one of the earlier works, but
shows what all of this is about (speaking and pop culture).</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Q63IA_jja8s" frameborder="0" allowfullscreen></iframe>
<p>Second, here’s <a href="http://kimmomodig.com/deep4u.html"><em>DEEP4U</em></a> (2014). It’s a collaboration between
Kimmo Modig, Georges Jacotey, Shawné Michaelain Holloway and Jennifer
Chan. Like a lot of Modig’s art, it’s very meta.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/uffZNO73qeE" frameborder="0" allowfullscreen></iframe>
<p>Finally, <a href="http://kimmomodig.com/helberg/"><em>Court of Helburg</em></a> (2014), a 90-minute play. It’s
hard to summarize, but basically Kimmo Modig has gotten your attention
for 90 minutes so he might as well entertain you. It felt to me
like start-up comedy for adults (in the sense of mature taste, not
sexual content).<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/mnJUe9sMuPg" frameborder="0" allowfullscreen></iframe>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I don’t think any of you will actually watch this through, but
hey. I did watch it even though I was in the premiere. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
A Mind for Numbers
https://quanttype.net/p/mind-for-numbers/
Mon, 29 Jun 2015 00:00:00 +0000https://quanttype.net/p/mind-for-numbers/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mfn_hu_41a0bc26fdbaee25.webp" type="image/webp" />
<img src="https://quanttype.net/images/mfn.jpg" width="600" height="337.2" alt="E-books are not very good for product shots." loading="lazy" />
</picture>
</figure>
<p>I’ve never been very good at studying. This point was driven home by
my latest exam. I studied for it for many hours, spanned over a month
and it was about a topic I like (abstract algebra). Yet I failed the
exam.</p>
<p>It was not the first exam I’ve failed, but what makes it poignant is
that I really spent a lot of energy in studying for no result. So what
went wrong?</p>
<p>To answer that, I read a book on math study technique. The one I
picked was <a href="http://www.barbaraoakley.com/mfn.html"><em>A Mind for Numbers</em></a> by <em>Barbara Oakley</em>. It deals
with topics like self-testing, chunking and procrastination. The book
is practical, but it has solid grounding in cognitive science. It
confirmed to me many things I’ve thought about studying (including
some of my <a href="https://quanttype.net/posts/2014-10-21-four-scientific-learning-hacks.html">study hacks</a>) - even if I haven’t been able to put
them in practice.</p>
<p>My main takeaway is that constant self-testing is the key for deeply
learning mathematics. When you’ve read something, put away the book
and try to summarize it. Try to reproduce key definitions and
proofs. If you can’t explain your learnings to yourself, have you
really learned them? Dr. Oakley writes:</p>
<blockquote>
<p>”In the same amount of time, by simply practicing and recalling the
material, students learned far more and at a much deeper level than
they did using any other approach.”</p>
</blockquote>
<p>Another key point is the importance of building solid foundations. By
building a library of basic knowledge, it’s easy to learn the more
advanced topics. Without the foundations, you can end up with brittle
knowledge: you can solve the formulaic exercises in the course or in
the book, but you can’t apply the knowledge anywhere else.</p>
<h2 id="so-what-about-that-exam">So what about that exam?</h2>
<p>It’s the latter point that I failed to account for. I was studying
after the work and I had very tight schedule, so I just pushed forward
without taking time understanding the basics. I ended up doing almost
all the exercises in the course, but without understanding anything.</p>
<p>Actually, I realized that things are going wrong already before the
exam and started reading the book. I went back, took all the cognitive
tricks and rehearsed the basics. It worked, but I only had time to
cover half of the topics. I aced the first half of the test and had no
idea what to with the other half. Alas.</p>
<h2 id="i-recommend-this-book">I recommend this book</h2>
<p>Failing an exam sucks, but it’s high time for me to learn to
study. This book gave me a good idea on how to better structure my
studies. I wish I had read it as a freshman, even though I doubt I
would’ve heeded the advice.</p>
Spend some time away from computers
https://quanttype.net/p/spend-some-time-away-from-computers/
Mon, 22 Jun 2015 00:00:00 +0000https://quanttype.net/p/spend-some-time-away-from-computers/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mist_hu_98575fb4cfb3468b.webp" type="image/webp" />
<img src="https://quanttype.net/images/mist.jpg" width="600" height="450" loading="lazy" />
<figcaption>The view from the veranda. (<a href="https://flic.kr/p/u32mib">details on Flickr</a>)</figcaption>
</picture>
</figure>
<p>I spent the last couple of days at a summer cottage. I had my phone
and laptop with me, but I didn’t use them at all even though it was
raining all the time. Instead I read books and played board games. It
was very relaxing.</p>
<p>I feel stupid for writing this down, but spending some time away from
the Internet is a great idea. I know it and I think you know it,
too. I also know that I sometimes forget it. It is the summer vacation
season, so I thought it’d be a good time for a gentle reminder.</p>
ROS: Good, bad and ugly
https://quanttype.net/p/ros-good-bad-and-ugly/
Mon, 15 Jun 2015 00:00:00 +0000https://quanttype.net/p/ros-good-bad-and-ugly/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/mimbo_hu_cc21892b6544aa97.webp" type="image/webp" />
<img src="https://quanttype.net/images/mimbo.jpg" width="600" height="398.4375" loading="lazy" />
<figcaption>I don’t think this cool little bot uses ROS. Photo CC-BY by <a href="https://flic.kr/p/b5cc2R">langfordw</a>.</figcaption>
</picture>
</figure>
<p>At work, we use <a href="http://www.ros.org">The Robot Operating System</a> is some parts of our
software. ROS is a software framework for building robot
applications. There’s a message passing system with C++ and Python
implementations and a bunch of ready-made robotics modules and
hardware wrappers that use the message passing system.</p>
<p>We’ve now used ROS for a while. It has been a success in some regards,
but also a source of frustration. Below I’ll look at some
of the issues.</p>
<p>This post is about the core of ROS. A major selling point for ROS is
the ecosystem of existing modules. We use almost none of them, so I
can’t comment on their quality.</p>
<h2 id="the-good">The good</h2>
<p>The message passing system itself is nice. It’s based on
publish-subscribe channels and works over TCP. There’s a
protobuf-style serialization format, a server for channel discovery
(<code>rosmaster</code>). There are also command-line
tools for inspecting, recording and replaying the messages.</p>
<p>Adopting a message passing system was a great idea. It’s now easy to
write new nodes, because there’s an easy and flexible way to
communicate with the existing system. Inspecting a running system by
looking at the message streams is handy, and you can also use the same
tools to look the recorded messages.</p>
<p>You could build a message passing system of your own, but it’s nice to
find an integrated package.</p>
<h2 id="the-bad">The bad</h2>
<p>ROS is very framework-y. It comes with its own build system and a tool
for running the applications developed with it (roslaunch). In theory,
you do not have to use them, but ROS is pretty much built around the
assumption that you do. Retrofitting ROS communication into an
existing applications can be painful. Prepare for yak shaving.</p>
<p>We’ve also had problem in building robust long-running systems with
ROS. There have been resource leaks and thread safety issues and we’ve
had problems with handling failures like rosmaster dying. I have a
feeling that this might be because there aren’t that many ROS users
building systems that actually need to be robust.</p>
<h2 id="the-ugly">The ugly</h2>
<p>There’s an unofficial, unmaintained Java port. The resource leaks were
so bad that we simply stopped using it.</p>
<h2 id="should-i-use-ros">Should I use ROS?</h2>
<p>If you’re considering ROS for a small project, like a school project,
go for it - especially if you can leverage the third-party modules for
ROS. You will get a lot of handy stuff pre-made and likely the bad
bits won’t bite you.</p>
<p>If you’re considering ROS for a bigger project, do your due
diligence. Are you able to deal with robustness issues if they come
up? Is supporting C++ and Python enough? The ideas behind ROS are good
but not unique. For a long-term project, handling problems should be a
bigger issue than getting stuff “for free”.</p>
The Law of Partial Test Coverage
https://quanttype.net/p/law-of-partial-testing-coverage/
Mon, 08 Jun 2015 00:00:00 +0000https://quanttype.net/p/law-of-partial-testing-coverage/<p>I’ve been thinking about unit tests lately. Here’s an observation I
made - I call it <em>the law of partial test coverage</em>:</p>
<blockquote>
<p>When your test coverage is less than 100%, the most annoying bugs
will be in the untested part.</p>
</blockquote>
<p>Why is this? An obvious reason is that untested code is more likely to
have regressions. A more subtle reason is that it’s not random what
parts of the code are tested and what parts are not.</p>
<p>Usually there are two kinds of untested code: trivial and hard to test.</p>
<p>Trivial code is not a problem. For example, in Python, maybe you
haven’t bothered to write tests for all your <code>__repr__</code> methods, or
maybe nothing exercises the <code>if __name__ == "__main__": main()</code>
line. If there’s a bug, you’ll likely spot it immediately.</p>
<p>The other kind of untested code is the tricky part: the code that is
hard to test. There might be some complex I/O operations involved, for
example. If the code is hard to test automatically, it’s often hard to
test manually. It might even be hard to understand.</p>
<p>This is means that you just won’t have any idea of whether your code
works after you make some changes. You will only find it out by
running the code in production. There will be bugs.</p>
Good math exercises build trust
https://quanttype.net/p/good-exercises-build-trust/
Mon, 01 Jun 2015 00:00:00 +0000https://quanttype.net/p/good-exercises-build-trust/<p>There’s this feeling you get when working on an interesting bug. You
just can’t stop until you’ve figured out what’s wrong. That’s also
what solving a good math exercise feels.</p>
<p>It’s not always the case. If you’re working on a bug - or a math
exercise - that is way beyond your skills, you’re in for a great deal
of frustration. You don’t know what to do and you don’t even know how
to find out what to do.</p>
<p>I’m working through a set of exercises in abstract algebra and it has
been frustrating. This got me thinking. In the spring, I enjoyed
working on topology and I used to like algebra. This course shouldn’t
be harder than the ones I’ve seen before. What’s wrong?</p>
<p>Some of the exercises required big leaps of understanding. They were
too hard for me. I tried to do them for a while, then went to look at
the solutions for hints. There were some steps that I wouldn’t had
thought of in any reasonable time!</p>
<p>This broke my trust in my own skills in doing these exercises. Now
every time I encounter a hard exercise, I’m wondering whether I can
actually solve it or if it again requires some clever trick I wouldn’t
think of. Then it’s way too easy to give up and I never get the
satisfaction of a solved problem.</p>
<p>To avoid this, exercises should grow in difficulty as you progress,
but not too fast. This way they build trust in your skills. Here’s an
induction axiom that an ideal exercise set should fulfill:</p>
<blockquote>
<p>If you solved exercises 1 to n, you can solve exercise n+1.</p>
</blockquote>
<p>Note: The said exercises and the material were meant to be used on a
lecture course, but I’m self-studying. Attending the lectures would
likely make the exercises work much better. The reason I picked this
material is that I’m going to take an exam based on it. Oh well.</p>
Quickly jumping between git branches
https://quanttype.net/p/switching-git-branches/
Sat, 23 May 2015 00:00:00 +0000https://quanttype.net/p/switching-git-branches/<p>When working on software with Git, I create a new branch for every change
request I’m going to make. This means that I might be working on a bunch of
branches in any repository. How to switch between them easily?</p>
<p>I use a small script to do it, called <code>git-b</code> (the name is inspired by
<a href="https://github.com/joelthelion/autojump">autojump’s</a> <code>j</code>). It gives you list of branches ordered by the time
of the latest commit. It uses <a href="https://github.com/garybernhardt/selecta">selecta</a> so you get to choose the
branch by typing a part of its name. It’s simple, but so much better than <code>git checkout <tab></code>, which is what I did before. (I imagine you could actually
configure zsh’s tab completion for similar experience. Alas.)</p>
<p>Here’s the code:</p>
<script src="https://gist.github.com/miikka/dda2a443f3a357c541f3.js"></script>
Emacs: Get the path for current buffer from command-line
https://quanttype.net/p/emacs-get-current-buffer-path/
Sun, 17 May 2015 00:00:00 +0000https://quanttype.net/p/emacs-get-current-buffer-path/<p>At work, I use Emacs (nowadays with the <a href="https://github.com/syl20bnr/spacemacs">Spacemacs</a>
configuration). My favorite Git history browser is <a href="http://jonas.nitro.dk/tig/">tig</a>. When I
want to see the git history or blame for the file I’m currently
editing, what do I do?</p>
<p>There are many possible solutions, but here’s mine: a small script
called <code>cur</code> that returns the path to the buffer currently active in
Emacs.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-haskell" data-lang="haskell"><span class="line"><span class="cl"><span class="o">#!/</span><span class="n">bin</span><span class="o">/</span><span class="n">sh</span>
</span></span><span class="line"><span class="cl"><span class="nf">emacsclient</span> <span class="o">-</span><span class="n">e</span> <span class="kt">'(buffer-file-name (window-buffer)</span><span class="p">)</span><span class="sc">'</span><span class="err"> | </span><span class="se">\</span>
</span></span><span class="line"><span class="cl"> <span class="n">sed</span> <span class="o">-</span><span class="n">e</span> <span class="n">'s</span><span class="o">/^</span><span class="s">"//' -e 's/"</span><span class="o">$//</span><span class="sc">'</span>
</span></span></code></pre></div><p>It’s easy to use with tig – or any other command-line tool you might
need:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">tig blame <span class="k">$(</span>cur<span class="k">)</span>
</span></span></code></pre></div>Birds and context-free grammars
https://quanttype.net/p/birdsongs-and-cfgs/
Mon, 27 Apr 2015 00:00:00 +0000https://quanttype.net/p/birdsongs-and-cfgs/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/swan_ocular_hu_233b0c65a99a69fc.webp" type="image/webp" />
<img src="https://quanttype.net/images/swan_ocular.jpg" width="600" height="450" loading="lazy" />
<figcaption>Swans are not known for their beautiful song. (<a href="https://flic.kr/p/siu9v7">details on Flickr</a>)</figcaption>
</picture>
</figure>
<p>A lecturer once claimed that birdsongs conform to a formal grammar. I
didn’t ask about it then, but I’ve thought about it many times
since.</p>
<p>Birdsongs and human languages have certain similarity. For example,
the brain structures used for producing them are similar, and both
humans and birds go through a “babbling” stage in their infancy when
learning to speak and sing. Could it be that birdsongs have
grammatically as complex structure as human languages?</p>
<p>While studying something else, I stumbled upon an article that
gestures towards the answer “no”.</p>
<ul>
<li>G. J. L. Beckers, J. J. Bolhuis, K. Okanoya, R. C. Berwick: <a href="http://alpha-leonis.lids.mit.edu/wordpress/wp-content/uploads/2014/04/BeckersNR2012.pdf"><em>Birdsong
neurolinguistics: songbird context-free grammar claim is
premature</em></a>. NeuroReport 23:139–145. 2012.</li>
</ul>
<p>There’s <a href="http://www.nature.com/neuro/journal/v14/n8/full/nn.2869.html">an earlier paper</a> by Abe and Watanabe that claims that
<a href="http://en.wikipedia.org/wiki/Society_finch">Bengalese finches</a> can learn to discriminate songs based on a
context-free grammar (CFG). Beckers et al. claim that the experiment
design is flawed and the matching on acousting similarity is more
likely explanation. My understanding is that this is the general state
of the research: there have been attempts to show that birds can learn
CFGs, but the so far the evidence is not convincing.</p>
<p>There’s even a book called <a href="http://mitpress.mit.edu/books/birdsong-speech-and-language-0">Birdsong, Speech, and Language</a> with
several chapters on this issue. I haven’t read it, but maybe it would
have some answers for me.</p>
<hr>
<p>My hunch is that many birdsongs have regular grammar. This gave me an
idea that maybe you could generate novel birdsongs with a Markov
chain. Split a large collection of song recordings into syllables,
cluster them based on the acoustic similarity, create a Markov chain
from them and produce new songs.</p>
<p>When I tried to do this, I wasn’t able to figure out the clustering. I
think it could be done if I just knew a bit more about pattern
recognition. I made <a href="http://vapaus.org/birdchain/">this webtoy</a>, though.</p>
You should take lecture notes with pen and paper
https://quanttype.net/p/take-pen-and-paper-notes/
Tue, 21 Apr 2015 00:00:00 +0000https://quanttype.net/p/take-pen-and-paper-notes/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/the_setup_hu_4d6eb03a944af3db.webp" type="image/webp" />
<img src="https://quanttype.net/images/the_setup.jpg" width="600" height="450" loading="lazy" />
<figcaption>My highly advanced note-taking setup. (<a href="https://www.flickr.com/photos/arctan/16604676043/">details on Flickr</a>)</figcaption>
</picture>
</figure>
<p>Is it a good idea to use a laptop to take lecture notes instead of
writing them on paper?</p>
<p>My personal experience says no. There’s the allure of the Internet,
and looking around in a classroom, I’m not the only one who can’t
always resist it. Even when I do avoid the distractions, my
concentration feels more superficial than when using paper and
pen. Taking mathematical notes has the extra problem of writing down
the notation and capturing the explanatory drawings - both are
straightforward on paper.</p>
<p>Anecdotes are fun, but is there any research to back this claim?
Here’s a study<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> that agrees with me:</p>
<ul>
<li>P. A. Mueller, D. M. Oppenheimer: <a href="http://www.hepl.ch/files/live/sites/systemsite/files/filiere-bp/programme-formation/the_pen_is_mightier_than_the_keyboard.pdf"><em>The Pen Is Mightier Than the
Keyboard: Advantages of Longhand Over Laptop Note
Taking</em></a>. Psychological Science. 2014-04-23.</li>
</ul>
<p>They acknowledge the risk of distractions, but even when the
distractions are avoided, they claim that laptop notes are worse:</p>
<blockquote>
<p>The present research suggests that even when laptops are used solely
to take notes, they may still be impairing learning because their
use results in shallower processing.</p>
</blockquote>
<p>This is because laptop users tend to transcribe lectures verbatim,
whereas longhand users have to put it in their own words, because they
write slower with a pen than with a keyboard. The cognitive processing
required for reframing the information is the key for better recall.
(This is also mentioned in my <a href="http://quanttype.net/posts/2014-10-21-four-scientific-learning-hacks.html">scientific learning hacks</a>!)</p>
<p>To me, taking useful notes seems like a skill that takes practice. The
article does not discuss this. My gut feeling is that if you’re
mindful about results like this, you can become very efficient at
taking notes with laptop. In the second experiment described in the
article, laptop users were asked to avoid verbatim notes. This didn’t
help much, but maybe you can do better, because you understand why it
matters.</p>
<p>Myself, I’m going to continue with pen and paper. In theory,
computerized notes are great - they’re searchable and everything! -
but in practice I haven’t found many benefits. They’re a bit easier to
write, but that easiness is what makes them more shallow.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="http://slatestarcodex.com/2014/12/12/beware-the-man-of-one-study/">You should never trust just one study…</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Tornien taisto is coming soon
https://quanttype.net/p/tornien-taisto/
Tue, 14 Apr 2015 00:00:00 +0000https://quanttype.net/p/tornien-taisto/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/joutsen_hu_4cde39fcbc1e749d.webp" type="image/webp" />
<img src="https://quanttype.net/images/joutsen.jpg" width="600" height="337.5" loading="lazy" />
<figcaption>Here’s a bird I saw last Sunday. (<a href="https://www.flickr.com/photos/arctan/17093268686/">details on Flickr</a>)</figcaption>
</picture>
</figure>
<p>The yearly Finnish birdwatching competition <a href="http://www.birdlife.fi/tornientaisto/">Tornien taisto</a>
(<em>Battle of the Towers</em>) is coming in early May. This will be the
fifth time I’m taking part.</p>
<p>Here’s the concept: around Finland, teams of at least three people go
to birdwatching towers for eight hours and try to identify as many
bird species as possible. The best teams spot over hundred species -
our team’s score has usually been around 40. The competition takes
place when the migratory birds are migrating to Finland for the
summer, so there should be lots of them to be seen and heard.</p>
<p>Personally I’m not very good at identifying birds - I’ve teamed up
with some friends who are quite a bit better. Still, I’ve been
practicing the songs of some birds. Last year, my proudest moment was
to identify <a href="https://en.wikipedia.org/wiki/Redwing">a redwing</a> by ear. Finnish Wikipedia describes
redwing as “one of the most common birds in Finland”. I admit it’s a
small win, but hey, you have to start somewhere.</p>
<p>Tornien taisto isn’t very serious competition. For me, it’s more about
spending a day in the nature, having good time and learning about
birds. I recommend taking part. If you aren’t up for the competition,
you could still visit a tower - <a href="http://www.birdlife.fi/tornientaisto/tt/yhdistystornit.php">some of the towers</a> even
organize guided tours.</p>
New theorem prover: Lean
https://quanttype.net/p/lean-theorem-prover/
Tue, 07 Apr 2015 00:00:00 +0000https://quanttype.net/p/lean-theorem-prover/<p>Microsoft Research and CMU are developing a new theorem-prover,
<a href="http://leanprover.github.io/">Lean</a>. Here’s how they describe their goals:</p>
<blockquote>
<p>The Lean Theorem Prover aims to bridge the gap between interactive
and automated theorem proving, by situating automated tools and
methods in a framework that supports user interaction and the
construction of fully specified axiomatic proofs. The goal is to
support both mathematical reasoning and reasoning about complex
systems, and to verify claims in both domains.</p>
</blockquote>
<p>It looks similar to Coq and Agda, but I’m not enough of an expert to
provide a comparison.</p>
<p>What I find interesting is that in addition to a traditional,
Emacs-based proof development environment,
<a href="https://leanprover.github.io/tutorial/?live">it also works in the browser</a>. This should lower the barrier
of entry for new users - I’ve seen a lot of complaints about how hard
e.g. Coq is to set up (<a href="http://quanttype.net/posts/2015-01-17-coq-proof-general-on-osx.html">see my instructions</a> if you’re
using OS X).</p>
<p>I haven’t yet played around with it, but I hope to take a look at
<a href="https://leanprover.github.io/tutorial/index.html">the tutorial</a> soon.</p>
The value of MOOCs is not in the videos
https://quanttype.net/p/value-of-moocs-is-not-in-the-videos/
Tue, 31 Mar 2015 00:00:00 +0000https://quanttype.net/p/value-of-moocs-is-not-in-the-videos/<p>In the article <em><a href="https://www.class-central.com/report/why-my-mooc-is-not-built-on-video/">Why My MOOC is Not Built on Video</a></em>,
Prof. Lorena A. Barba writes:</p>
<blockquote>
<p>The participants of #NumericalMOOC will have noticed that we made
only one video for the course. I thought that maybe I would do a
handful more. But in the end I didn’t and I don’t think it matters
too much.</p>
</blockquote>
<p>I haven’t been very interested in MOOCs, because I’ve seen them as a
glorified collections of lecture videos. I seldom find lectures
useful, and in video lectures you can’t even ask questions.</p>
<p>This article made me change my mind. Instead of collections of videos,
I now see MOOCs as potential collections of exercises. As Prof. Barba
writes:</p>
<blockquote>
<p>Videos are nice, they can get you exposed to a new concept for the
first time in an agreeable way, but they do not produce learning, on
their own. Students need to engage with the concepts in various
ways, interact with ideas and problems, work through a process of
“digestion” of the learning material.</p>
</blockquote>
<p>When learning a new topic by myself, one of the hard things is to find
a useful progression of challenges to tackle. The challenges should be
hard enough to be interesting, but easy enough to not be
frustrating. For me, in the offline math courses the exercise sheets
are the most valuable part of the course. MOOCs can and do provide the
same value.</p>
<p>I do not know why I didn’t realize this earlier.</p>
<hr>
<p>Speaking of MOOCs, if you want to learn functional programming, check
out University of Helsinki’s course
<a href="http://mooc.fi/courses/2014/clojure/">Functional programming with Clojure</a>. There are no lecture
videos, but there are exercises. I took the course when it was an
offline course and the exercises were great.</p>
Are quantum computers faster than classical computers?
https://quanttype.net/p/are-quantum-computers-faster-than-classical/
Sat, 21 Mar 2015 00:00:00 +0000https://quanttype.net/p/are-quantum-computers-faster-than-classical/<p>We do not know - not from the point of view of computational
complexity, anyway.</p>
<p>The complexity class $\textrm{BQP}$ (bounded-error quantum
polynomial-time) roughly corresponds to the problem that can be solved
efficiently with a quantum computer, the same way as problems in
$\textrm{P}$ can be solved efficiently with a classical computer. BQP
is essentially the quantum version of $\textrm{BPP}$ (bounded-error
probabilistic polynomial-time). For more precise description, see
<a href="http://www.scottaaronson.com/democritus/lec10.html">Scott Aaronson’s lecture notes</a> or <a href="https://en.wikipedia.org/wiki/BQP">Wikipedia</a>.</p>
<p>The question in the title corresponds to the question of whether
$\textrm{P}$ is a strict subset of $\textrm{BQP}$. The following
hierarchy is known:</p>
<p>$$\textrm{P} \subseteq \textrm{BQP} \subseteq \textrm{PSPACE}$$</p>
<p>The strictness of these relations is an open question. Proving that
quantum computers are faster than classical computers would imply that
$\textrm{P} \subsetneq \textrm{PSPACE}$, which would be a breakthrough
result in classical complexity theory!</p>
<p>Of course, the existence of Shor’s algorithm and some other
algorithms that are faster than their classical equivalents do
support the idea that quantum computers are more powerful than
classical computers. A proof of the contary result would be a
surprise.</p>
<p>What about $\textrm{NP}$? We do not know much about the relationship
between $\textrm{BQP}$ and $\textrm{NP}$. It is clear that, contrary
to the popular culture description, quantum computers won’t allow us
to magically solve NP-complete problems efficiently. Scott Aaronson
writes in the <a href="http://www.scottaaronson.com/democritus/lec10.html">lecture notes</a>:</p>
<blockquote>
<p>[A] quantum computer is not a device that could “try every possible
solution in parallel” and then instantly pick the correct one. If we
insist on seeing things in terms of parallel universes, then those
universes all have to “collaborate” – more than that, have to meld
into each other – to create an interference pattern that will lead
to the correct answer being observed with high probability.</p>
</blockquote>
This Week in Finnish Politics
https://quanttype.net/p/this-week-in-finnish-politics/
Sat, 14 Mar 2015 00:00:00 +0000https://quanttype.net/p/this-week-in-finnish-politics/<p>Today was the last workday of the Parliament of Finland before the
election break. This final week was so eventful that I had to write
this post to make sense of it.</p>
<h2 id="healthcare-for-paperless-immigrants">Healthcare for paperless immigrants</h2>
<p>On Monday, the cabinet proposed a law that would guarantee a minimum
level of healthcare for paperless immigrants. The law was prepared by
Minister of Health and Social Services <em>Susanna Huovinen</em>, of the
Social Democratic Party. The law would have been likely approved,
except that MP <em>Kari Rajamäki</em>, also of SDP, demanded postponing the
vote. Because this is the last week of the Parliament, there was no
time for another vote and so the law was abandoned.</p>
<p>Mr. Rajamäki, after 32 years in the Parliament, is not going to run
for the Parliament again. He must be proud of his last act as a member
of Parliament.</p>
<h2 id="limiting-student-benefits">Limiting student benefits</h2>
<p>The cabinet proposed taking away the student benefits for studying for
a second degree of the same level that one already has. The proposal
was widely critisized by the student movement, but it was nevertheless
initally approved on Tuesday by <a href="http://www.eduskunta.fi/triphome/bin/thw.cgi/trip/?$%7BAPPL%7D=aanestysu&$%7BBASE%7D=aanestysu&$%7BTHWIDS%7D=66.12/1426340472_14819&$%7Boohtml%7D=aax/hex5000&$%7Bhtml%7D=aax/aax5000&$%7Bsnhtml%7D=aax/aaxnosyn&$%7Bsavehtml%7D=/thwfakta/aanestys/aax/aax.htm">votes 95-91</a>.</p>
<p>On Thursday, a change to the law about the
<a href="https://en.wikipedia.org/wiki/Sami_Parliament_of_Finland">Sami Parliament of Finland</a> was withdrawn by the cabinet. MPs
of SDP (which is a member of the cabinet coalition) announced that
they would also postpone the ratification of ILO Convention 169.</p>
<p>This pissed off the Swedish People’s Party (also member of the cabinet
coalition) and they announced on Friday that they’d vote against the
student benefit law in the final vote. Almost immediately, National
Coalition Party (yet another member of the cabinet) announced that
they will follow suit. In turn, SDP announced that they’d vote against
the second-level education budget cuts proposed by the cabinet.</p>
<p>Today, in <a href="http://www.eduskunta.fi/triphome/bin/thw.cgi/trip/?$%7BAPPL%7D=aanestysu&$%7BBASE%7D=aanestysu&$%7BTHWIDS%7D=3.12/1426340472_14819&$%7Boohtml%7D=aax/hex5000&$%7Bhtml%7D=aax/aax5000&$%7Bsnhtml%7D=aax/aaxnosyn&$%7Bsavehtml%7D=/thwfakta/aanestys/aax/aax.htm">the final vote</a>, the parliament voted against the
student benefit law 185-1. Only <em>Kimmo Sasi</em> (NCP) voted for it. He
commented that he is fed up with all the budget cuts being flushed
down the toilet.</p>
<p>Prime Minister <em>Alexander Stubb</em> (NCP) commented that the coalition
government is not in chaos or collapsed. Yeah right.</p>
Types of JavaScript
https://quanttype.net/p/types-of-javascript/
Sat, 31 Jan 2015 00:00:00 +0000https://quanttype.net/p/types-of-javascript/<p>Ever since I heard about <a href="http://isomorphic.net">isomorphic JavaScript</a>, I’ve been
wondering what other morphisms describe JavaScript. Here’s a brief
summary of my findings:</p>
<dl>
<dt>Isomorphic JavaScript</dt>
<dd>
<p>The same code can be run on the server and in the browser.</p>
</dd>
<dt>Endomorphic JavaScript</dt>
<dd>
<p>The server/client code can be run with another server/client runtime.</p>
</dd>
<dt>Epimorphic JavaScript</dt>
<dd>
<p>The same code can be run in all the browsers. Also known as epic JavaScript.</p>
</dd>
<dt>Homeomorphic JavaScript</dt>
<dd>
<p>Isomorphic JavaScript that is continuously deployed.</p>
</dd>
<dt>Catamorphic JavaScript</dt>
<dd>
<p>Isomorphic JavaScript, where the server code is minified before it
is run in the browser.</p>
</dd>
<dt>Homotopic JavaScript</dt>
<dd>
<p>Isomorphic JavaScript, where the server code is transpiled before it
is run the browser.</p>
</dd>
</dl>
Setting up Coq, Ssreflect and Proof General on OS X
https://quanttype.net/p/coq-proof-general-on-osx/
Sat, 17 Jan 2015 00:00:00 +0000https://quanttype.net/p/coq-proof-general-on-osx/<p>Previously I’ve used CoqIDE to interact with Coq, because I figured
out it’d be tricky to set up <a href="http://proofgeneral.inf.ed.ac.uk">Proof General</a>. Proof General the
Emacs interface to Coq and other interactive theorem provers. This
time I decided to take the PG route and turns out it’s really easy if
you’re using <a href="http://brew.sh">Homebrew</a>! While at it, I also set up
Ssreflect, which is an enhanced version of Coq with alternative
standard library.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install emacs coq ssreflect
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Use --with-emacs to point to the Emacs you're using, so that the elisp</span>
</span></span><span class="line"><span class="cl"><span class="c1"># files get compiled with the correct Emacs version.</span>
</span></span><span class="line"><span class="cl">brew install proof-general --with-emacs<span class="o">=</span>/usr/local/bin/emacs
</span></span></code></pre></div><p>Next, do as Homebrew tells you and put these lines into your <code>~/.emacs</code>:</p>
<pre tabindex="0"><code>(load-file "/usr/local/share/emacs/site-lisp/ProofGeneral/generic/proof-site.el")
(load-file "/usr/local/Cellar/ssreflect/1.5/share/ssreflect/pg-ssr.el")
</code></pre><p>Finally, customize Emacs variable <code>coq-prog-name</code> to the path of
<code>coqtop</code>, i.e. <code>/usr/local/bin/coqtop</code>.</p>
<h1 id="spacemacs">Spacemacs</h1>
<p><em>This section was added 2016-02-27.</em></p>
<p>If you’re using <a href="http://spacemacs.org/">Spacemacs</a>, there’s now a very simple
<a href="https://github.com/olivierverdier/spacemacs-coq">Coq layer</a> available. It sets up Homebrew-installed Proof General,
although it does not support Ssreflect.</p>
<p>First, clone the layer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/olivierverdier/spacemacs-coq ~/.emacs.d/private/coq
</span></span></code></pre></div><p>Then, add <code>coq</code> to <code>dotspacemacs-configuration-layers</code>. You can edit your
<code>.spacemacs</code> with <code>SPC f e d</code> and reload it with <code>SPC f e R</code>.</p>
Yearnote 2014
https://quanttype.net/p/yearnote-2014/
Mon, 05 Jan 2015 00:00:00 +0000https://quanttype.net/p/yearnote-2014/<p>Here’s what I did in 2014.</p>
<h2 id="working-and-studying">Working and studying</h2>
<p>In spring, I concentrated on my work as a software developer. Then I decided to
finally graduate from the university and asked my employer for study leave from
September till April. In summer, I worked even more to save up some money for
studying. In autumn, I studied more than ever. That brought me over 55 course
credits (the usual rate is about 30 per semester). I’m happy I finally found
success with studies - my earlier semesters have been much slower.</p>
<h2 id="travel-and-events">Travel and events</h2>
<ul>
<li>Visited Stockholm thrice. Highlights: having coffee at <a href="http://www.dropcoffee.com">Drop Coffee</a>
and discovering <a href="http://www.adisgladis.se">Adisgladis</a>, the lifestyle store right next to Drop
Coffee.</li>
<li>Visited Tartu to celebrate <em>volbripäev</em> with <a href="http://www.raimla.ee">Raimla</a>. For the
record: <a href="http://www.tartustudentvilla.hostel.com">Tartu Student Villa</a> is a pretty cool hostel.</li>
<li>Visited the Isle of Anglesey in Wales. Highlights: walking about <a href="https://en.wikipedia.org/wiki/Holyhead_Mountain">Holyhead
Mountain</a>, seeing a grey seal on a <a href="https://en.wikipedia.org/wiki/Puffin_Island_(Anglesey)">Puffin Island</a> cruise</li>
<li><a href="https://quanttype.net/posts/2014-09-07-emf2014.html">Attended Electromagnetic Field 2014</a>, the hacker camp in Bletchley, UK.</li>
<li><a href="https://quanttype.net/posts/2014-11-26-clojutre-2014.html">Attended ClojuTRE 2014</a> in Tampere, Finland.</li>
</ul>
<h2 id="stuff-made-by-me">Stuff made by me</h2>
<ul>
<li>Made <a href="http://vapaus.org/flappy-sine/">Flappy Sine</a> the game.</li>
<li><a href="https://quanttype.net/posts/2014-10-23-photo-practice-retro.html">Practiced photography</a>.</li>
<li>Fixed <a href="https://quanttype.net/posts/2014-11-29-guts-of-the-golden-pig.html">the golden pig</a>.</li>
<li>Posted 27 times on quanttype.</li>
</ul>
<h2 id="best-of">Best of</h2>
<p>Here are some things I was impressed by in 2014:</p>
<ul>
<li>Best album: Merelle by Pikku Kukka</li>
<li>Best back-up software: <a href="http://www.haystacksoftware.com/arq/">Arq</a></li>
<li>Best coffee shop: <a href="http://www.freesecoffee.fi">Freese Coffee Co.</a></li>
<li>Best low-alcohol beer: BrewDog’s <a href="http://www.brewdog.com/product/nanny-state">Nanny State</a></li>
<li>Best month of abstinence: <em>hevoseton helmikuu</em></li>
<li>Best performance art: <a href="http://kimmomodig.com/helberg/">Court of Helburg</a> by Kimmo Modig</li>
<li>Best restaurant lunch: mac’n’cheese in <a href="http://juuri.fi">Juuri</a></li>
<li>Best wine: Mouth Bomb by Winepunk</li>
</ul>
<p>Not awarded: best movie, best book, best hamburger. I didn’t read enough good
books, watch enough good films or eat good hamburgers.</p>
<h2 id="plans-for-2015">Plans for 2015</h2>
<p>This year I hope to work a bit less than last year, and to have more time to
make interesting stuff. Some plans I have for this year:</p>
<ul>
<li>Graduate as a Bachelor of Science.</li>
<li>Have a summer vacation that is at least two weeks long in one go.</li>
<li>See a wild hedgehog. (I live in one of the most urban spots of Finland…)</li>
<li>Read <a href="https://en.wikipedia.org/wiki/Infinite_Jest">Infinite Jest</a>.</li>
</ul>
Ambronite mini-review
https://quanttype.net/p/ambronite-mini-review/
Wed, 17 Dec 2014 00:00:00 +0000https://quanttype.net/p/ambronite-mini-review/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/ambronite_hu_ed51abaeab76a17d.webp" type="image/webp" />
<img src="https://quanttype.net/images/ambronite.jpg" width="600" height="374.7" alt="A glass of Ambronite" loading="lazy" />
</picture>
</figure>
<p><em>Update: See also my <a href="https://quanttype.net/posts/2015-11-11-joylent-mini-review.html">Joylent mini-review</a>.</em></p>
<p>A while ago I backed <a href="http://ambronite.com/">Ambronite</a> on Indiegogo, mostly out of
curiosity. It’s a nutritional drink powder, similar to <a href="http://www.soylent.me/">Soylent</a>.
Today I received my batch of Ambronite and I’ve just consumed the first
portion. Let me tell you about it.</p>
<p>It’s green and thick and looks nice. It smells good, too, like sawdust or bran
– I like that. Unfortunately it also tastes like sawdust, and not in a good
way. It has the mouthfeel of porridge made of sawdust. The aftertaste is okay,
though, and it did leave me feeling full. I didn’t expect it to taste nice, but
I’m surprised how off-putting the taste and the texture are. Seriously.</p>
<p>Maybe adding something to the mix will make the taste more pleasant. There have
been reports of using juice instead of water, or adding banana. Maybe I can eat
it with yoghurt instead of brans. The crowdfunding campaign promised a recipe
booklet, but it isn’t done yet.</p>
<p>Oh well.</p>
Going to write a bachelor's thesis
https://quanttype.net/p/going-to-write-bachelors-thesis/
Tue, 16 Dec 2014 00:00:00 +0000https://quanttype.net/p/going-to-write-bachelors-thesis/<p>My aim is to finally graduate as a Bachelor of Science in mathematics in the
spring. To do that, I need to write a bachelor’s thesis. I’ve now agreed with
my advisor about the topic: formalizing mathematics with Coq. It’s a topic I
don’t yet know much about, and neither does my advisor. Still, I’ve been
interested in it for a long time and this seemed like a good opportunity to
capitalize on that interest.</p>
<p>It won’t be anything fancy, but I hope to make at least one useful contribution
to the science: establishing some terminology in Finnish. The thesis must be in
Finnish and not much has been written about the topic in Finnish before. Nobody
reads bachelor’s theses, so I’m thinking of publishing a Finnish glossary on
the web and improving the coverage in Finnish Wikipedia.</p>
Guts of the Golden Pig
https://quanttype.net/p/guts-of-the-golden-pig/
Sat, 29 Nov 2014 00:00:00 +0000https://quanttype.net/p/guts-of-the-golden-pig/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kultasika_hu_fbf2581dfad89f50.webp" type="image/webp" />
<img src="https://quanttype.net/images/kultasika.jpg" width="600" height="374.7211895910781" loading="lazy" />
</picture>
</figure>
<p>Last year <a href="http://www.omstartdesign.fi/">Salla</a> made an interactive sculpture as a
school project. <a href="http://kultasika.tumblr.com">It’s a golden pig</a>. You drop a
coin in the coin slot, it prints you a receipt with wisdom about money out of
its mouth. Here’s a video:</p>
<div class="instaembed"><blockquote class="instagram-media" data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: auto; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://instagram.com/p/vyA_d6CMJ9/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_top">Video, jonka Miikka (@arcatan) julkaisi</a> <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2014-11-24T11:53:14+00:00">Marras 11, 2014 at 3:53 PST</time></p></div></blockquote> <script async defer src="//platform.instagram.com/en_US/embeds.js"></script></div>
<p>I cobbled together the electronics. The pig was re-exhibited at the <a href="http://designresearch.aalto.fi/events/aor2014/">
Art of Research</a> conference and
right now it is at <a href="http://tokyo.fi/tapahtumat/tokyon-joulumyyjaiset/?lang=en">TOKYO’s Christmas
Sales</a>. Before the
exhibition we gave the internals a small update and I thought it’d be nice to
write down how it works.</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kouru_hu_cc71ec88faa0e0de.webp" type="image/webp" />
<img src="https://quanttype.net/images/kouru.jpg" width="600" height="450" alt="Coin detector 1.1, Arduino and Raspberry Pi." loading="lazy" />
</picture>
</figure>
<p>Here are the parts:</p>
<ul>
<li>
<p>Coin detector is a light gate. There’s a red LED that points to <a href="http://en.wikipedia.org/wiki/Photoresistor">a
LDR</a>. They’re taped to a chute made out of plywood. The coin slot
funnels the coins to the chute and they roll through the light gate,
triggering the printing. The first version of the chute was made of
cardboard, but it was hard to align the LED and the LDR in a stable way.
Plywood is a huge improvement.</p>
</li>
<li>
<p>Arduino is used as an analog-to-digital converter. It reads the LDR in a loop
using the <a href="http://arduino.cc/en/Tutorial/AnalogInputPins">analog input pins</a>
and outputs every reading through the serial interface.</p>
</li>
<li>
<p>Python program on Raspberry Pi reads the measurements from Arduino over USB.
When the numbers go below certain threshold, it chooses a random PDF file
from a directory and calls <code>lpr filename.pdf</code>. In the first version, we had a
netbook, but I wanted to port this to Raspberry Pi since they’re so trendy.</p>
</li>
<li>
<p>The printer is <a href="http://www.starmicronics.com/printer/thermal_printers/tsp100">Star TSP143</a>, which works great on Linux, although
you have to install the drivers yourself. At first I was worried, because
there are <a href="http://www.starasia.com/tsp100driverdownload.asp">no drivers for Linux on ARM</a>, but no worries: the drivers
come with the source, which was trivial to compile. We chose this printer
because it was the only receipt printer we could borrow.</p>
</li>
</ul>
<p>For the new version, I made a program that draws a live graph of the signal,
for calibrating the threshold. This was another great improvement - previously
I just eyeballed the numbers in the console.</p>
<p>The original design was guided by the fact that I don’t know anything about
electronics. I decided to do the simplest thing that could work. Another
constraint was to use parts and materials we already had, since we didn’t want
to spend any money. It’s nothing fancy, but it works surprisingly well.</p>
<p>There is one known bug: it sometimes double or even triple-prints. I’ll fix it
for version 1.2.</p>
Event notes: ClojuTRE 2014
https://quanttype.net/p/clojutre-2014/
Wed, 26 Nov 2014 00:00:00 +0000https://quanttype.net/p/clojutre-2014/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/matopeli_hu_f88fc3d3efd0a4f6.webp" type="image/webp" />
<img src="https://quanttype.net/images/matopeli.jpg" width="600" height="374.7" alt="Immo Heikkinen rocking IDEA to code a snake game." loading="lazy" />
</picture>
</figure>
<p>Yesterday was <a href="http://clojutre.org/2014/">ClojuTRE 2014</a>. It’s the biggest (and
the only) Clojure conference in Finland. Here are my main impressions:</p>
<ul>
<li><strong>Wow, there are lots of people in Finland interested in Clojure</strong>, and they’re
actually using it in production. Clojure is one of the better programming
languages available right now and I’m glad that people have found it. Compare
this to the Haskell situation: there’s a lot of interest, but almost no-one
uses it for “serious” work.</li>
<li><strong>ClojureScript is at least as important as Clojure.</strong> Judging by the talks
and the chatter, people are very much into CLJS. Make sense: JavaScript is
everywhere and therefore ClojureScript can be everywhere.</li>
<li><strong>Haskell evangelists still have some work to do.</strong> Reactions to Bodil’s Haskell
talk ranged from “Haskell is the best” to “why on Earth would I use Haskell?”
I thought everybody already loved Haskell. Alas!</li>
</ul>
<p>The talks very pretty basic and there were some technical problems, but
otherwise everything went well. Since the talks were short (20-30 min), there
was a lot of variety, which was great. In general everybody seemed to have a
good time. Thanks to everyone involved!</p>
<h2 id="talks">Talks</h2>
<p>I’ve summarized the key points of each talk below. <em>Please note:</em> This was
written by me, not the presenters. If it sounds like they said something
stupid, it’s probably me putting words in their mouth.</p>
<ul>
<li>
<p><em>Wrapping Java in Awesomeness</em> (by Tero Kadenius & Juha Heimonen). While Java
is not the nicest language, there are good Java libraries out there. They can
be cumbersome to use, so you should wrap them nicely with Clojure. You’ll do
this by restricting mutation and feeding the Java objects with immutable
Clojure data.</p>
</li>
<li>
<p><em>Let’s Steal Some Java Goodness</em> (by Antti Virtanen). Clojure books tell you
how to write the <code>fib</code> function, but not how to do real-world stuff like
logging or performance monitoring. Inspired by <a href="http://projects.spring.io/spring-boot/">Spring
Boot</a>, Antti has created <a href="https://github.com/lokori/clj-weba">a Clojure
web app skeleton</a>) which covers these
and more, so you do not have to figure them out yourself.</p>
</li>
<li>
<p><em>How to Introduce Clojure to Your Organization</em> (by Erik Assum). Clojure is
elegant and expressive and it has REPL, which will blow your mind if you
haven’t seen it before. But how do you impress a programmer with deadlines?
Build a simple, valuable and non-critical project it with, like <a href="https://github.com/slipset/mybot">a useful
chatbot</a>.</p>
</li>
<li>
<p><em>Load Testing with core.async</em> (by Markus Hjort). To sneak in Clojure, Markus
has built a <a href="http://gatling.io">Gatling</a>-style tool for load testing, called
<a href="https://github.com/mhjort/clj-gatling">clj-gatling</a>. During the talk, Markus
showed us how to build the core logic of the tool using core.async. It was
straightforward.</p>
</li>
<li>
<p><em>Hacking FirefoxOS with ClojureScript</em> (by Timo Sulg). FirefoxOS applications
are developed with HTML5, which makes ClojureScript a first-class citizen on
the platform. There are some hurdles, like no REPL yet (because eval is
disabled), but there are likely workarounds. Since the Firefox OS phones are
so cheap (even if the specs are a bit dated), they make a great platform for
hacking projects.</p>
</li>
<li>
<p><em>A Clojurian’s Quest for Qt</em> (by Pauli Jaakkola). Using Qt with Clojure is
non-trivial: Qt Jambi is not maintained anymore and Qt Widgets for Node.js
does not really work. The way to do it is to use Qt Quick, QML and
ClojureScript. QML is no fun, but Pauli has created a Hiccup-style <a href="https://github.com/nilern/qdn">library
for generating QML</a>.</p>
</li>
<li>
<p><em>ClojureScript Live Coding With Ease Using Lively</em> (by Immo Heikkinen). By
live coding Immo means editing the live application by reloading code in the
running process (as opposed to playing music by writing code, which also
involves live coding in Immo’s sense). He has created the library
<a href="https://github.com/immoh/lively">Lively</a> for this purpose. Compared to the
alternatives like <a href="https://github.com/bhauman/lein-figwheel">figwheel</a>, it’s
simpler and more straightforward to use. Immo demoed Lively by building a
snake game. This was my favorite talk - fun and well presented!</p>
</li>
<li>
<p><em>Build Tooling With Boot</em> (by Juho Teperi). If you try to build a full-stack
web project (with Clojure, ClojureScript, LESS, …) with Leiningen, it soon
gets messy. <a href="http://boot-clj.com">Boot</a> is designed to solve this problem.
Boot tasks are Clojure functions which run in the same JVM. This makes them
faster and more composable than Leiningen tasks, and not every plugin has to
implement file-watching. There are still things missing like IDE support and
some common tasks.</p>
</li>
<li>
<p><em>Live Hacking with Unity 3D</em> (by Tims Gardner). There were some technical
problems with this presentation, but basically it was Tims demoing
<a href="https://github.com/arcadia-unity/Arcadia">Arcadia</a>, a Clojure interface for
Unity.</p>
</li>
<li>
<p><em>Hay - an embedded language</em> (by Meikel Brandmeyer). Sometimes you want to
make your application extensible by users. Exposing the host language - like
XMonad does - requires the users to have the full toolchain and expertise in
the host language. That’s why a simpler scripting language might be
warranted. Meikel is working on <a href="https://github.com/pinky-editor/hay">Hay</a>, a
stack-based concatenative language that is closely integrated with Clojure.
Meikel walked use through the basics of Hay and also showed us a useful use
of Clojure metadata: storing the stack signatures of Hay functions.</p>
</li>
<li>
<p><em>Haskell for Clojurists</em> (by Bodil Stokke). The Clojure community has been
open to the influences of other programming languages - as shown by e.g.
core.logic and core.typed. Bodil thinks that a good language to learn from is
Haskell (I concur) and she walked us through implementing linked lists up to
their Foldable instance.</p>
</li>
</ul>
A photo essay: Haronmäki
https://quanttype.net/p/a-photo-essay/
Mon, 17 Nov 2014 00:00:00 +0000https://quanttype.net/p/a-photo-essay/<p>I made you a photo essay titled <a href="http://vapaus.org/haronmaki">Haronmäki</a>. By
photo essay, I mean a short series of photos that are to be viewed as a whole.</p>
<p>I’m not sure if a HTML page of huge JPEGs is the best way to deliver.
Suggestions welcome.</p>
Customizing Nix packages
https://quanttype.net/p/customizing-nix-packages/
Fri, 07 Nov 2014 00:00:00 +0000https://quanttype.net/p/customizing-nix-packages/<p>I’m using <a href="http://nixos.org/nix/">Nix</a> to install some packages on a host where
I don’t have root and do not want to pester the admins all the time. The good
side of this is that I can install packages with one command. The bad side is
that installing anything takes forever. Because I use <a href="https://nixos.org/wiki/How_to_install_nix_in_home_(on_another_distribution)">a custom store
location</a>, I can’t use the binary packages. Nix builds <em>all</em>
the dependencies (including e.g. gcc and glibc), which takes a while.</p>
<p>Some Nix packages can customized. For example, I wanted to install
<a href="http://ikiwiki.info">ikiwiki</a> and use it with git. If you look at <a href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/ikiwiki/default.nix">ikiwiki’s
Nix expression</a>, you’ll see that git support is disabled by
default. How does one enable it?</p>
<p><a href="https://nixos.org/wiki/Nix_Modifying_Packages#Overriding_Existing_Packages">The documentation on this</a> can be hard to find, but it does
exist<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The gist of it is to create the file <code>~/.nixpkgs/config.nix</code> and
override the settings there. Here’s how I enabled ikiwiki’s git support:</p>
<pre><code>{
packageOverrides = pkgs: with pkgs; {
ikiwiki = ikiwiki.override {
gitSupport = true;
};
};
}
</code></pre>
<p>Now you can install ikiwiki with <code>nix-env -i ikiwiki</code> and git will be pulled
in.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Mostly you’ll find mentions about a file called <code>configuration.nix</code>, but as far as I can tell, that only applies if you’re using NixOS. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Did taking photos teach me anything?
https://quanttype.net/p/photo-practice-retro/
Thu, 23 Oct 2014 00:00:00 +0000https://quanttype.net/p/photo-practice-retro/<p>My <a href="https://quanttype.net/posts/2014-09-27-practicing-photography.html">28 days of practising photography</a> are now over. You can see the photos <a href="https://www.flickr.com/photos/arctan/sets/72157647570550377">on Flickr</a>. Did I learn anything?</p>
<p>My goals were to think about the photos I take, learn the basics of taking an
urban landscape shot and take at least one shot I could be proud of. The last
goal first, here’s the one shot I really like:</p>
<p><a href="https://www.flickr.com/photos/arctan/15416563259/in/set-72157647570550377">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/john-stenbergin-ranta_hu_1df3ff6dd55a32a4.webp" type="image/webp" />
<img src="https://quanttype.net/images/john-stenbergin-ranta.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</a></p>
<p>I didn’t really plan this shot. I was trying to take <a href="https://www.flickr.com/photos/arctan/15569797656/in/set-72157647570550377">another shot of the
university buildings on Siltavuorenpenger</a>. I had even brought a
tripod, but there just wasn’t enough light at 23 o’clock. Before going home, I
decided to take a quick snap at Pitkäsilta.</p>
<p>The project did improve my skills, but I can’t offer a coherent theory of the
basics of urban landscape photography. I wish I could, but it has become
apparent that I have even less idea of what I’m doing than I thought.</p>
<p>Nevertheless, here are some ideas for my novice peers:</p>
<ul>
<li>Everybody loves <a href="https://en.wikipedia.org/wiki/Perspective_(graphical)#One-point_perspective">one point perspective</a>. (<a href="https://www.flickr.com/photos/arctan/15376821326/in/set-72157647570550377">example</a>)</li>
<li>Separating the ground from the foreground would be great, but I can’t do it
with my camera. (<a href="https://www.flickr.com/photos/arctan/15224447100/in/set-72157647570550377">example</a> of me not doing it)</li>
<li>Everything looks more dramatic at night. (example: <a href="https://www.flickr.com/photos/arctan/15311233570/in/set-72157647570550377/">night</a> vs. <a href="https://quanttype.net/images/kaisatalo.jpg">day</a>)</li>
<li>Re-trying a shot on another day is a good idea. <a href="https://www.flickr.com/photos/arctan/15608018512/in/set-72157647570550377/">This one</a> was the
third time I took a photo at this place.</li>
<li>That contrast slider in your photo editing program is really tempting. Try
not to overuse it.</li>
<li>Take a lot of photos. You’ll accidentally take some good ones.</li>
</ul>
<p>I uploaded some of my photos on <a href="https://500px.com/arcatan">500px</a>. 500px’s algorithm seems to be
very good at putting my (a new user?) photos in front of people, since I
received a lot of likes. If you like social media likes, consider 500px.</p>
<p>Taking a single photo is great for photography-as-visual-art, but not so great
for photography-as-storytelling. It’s not like you can’t tell a story with a
single picture, it’s just that using more pictures often leads to better
results. Hemingway’s <a href="https://en.wikipedia.org/wiki/For_sale:_baby_shoes,_never_worn">six-word novel</a> is cool, too, but there
are benefits in longer-form writing. My 28 photos do not form a whole and they
do not tell a story. It wasn’t the purpose, either, but it might be what I want
to explore next.</p>
<p>As a bonus, here’s my best color shot:</p>
<p><a href="https://500px.com/photo/85906631/pengerkatu-by-miikka-koskinen">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pengerportaat_hu_f5098d0f4a0e9c02.webp" type="image/webp" />
<img src="https://quanttype.net/images/pengerportaat.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</a></p>
Four scientific ways to hack your learning
https://quanttype.net/p/four-scientific-learning-hacks/
Tue, 21 Oct 2014 00:00:00 +0000https://quanttype.net/p/four-scientific-learning-hacks/<p>During my university studies, I haven’t been to any book examinations until
now. I’m studying a minor in cognitive science and there’s a bunch of book
examinations I need to take.</p>
<p>The book I’ve now studied is Fundamentals of Cognition (Michael Eysenck, 2012).
I’ve been worried, because I don’t have any practice in studying for a book
examination. Luckily this book covers topics such as memory and learning! What
does cognitive psychology say about studying efficiently?</p>
<h2 id="process-the-information-deeply">Process the information deeply</h2>
<p>According to the <a href="https://en.wikipedia.org/wiki/Levels-of-processing_effect">level-of-processing theory</a>, the deeper your analyze the
information you’re learning, the longer-lasting the memory traces are.</p>
<p>Do not just read the material - do something with it. For example, put the
key points in your own words. This forces you to process the text
semantically instead of just glossing over. The best thing you can do is to
relate the information to yourself somehow - this gives an extra boost to
recollection.</p>
<h2 id="test-your-learning">Test your learning</h2>
<p>Research says that the long-term retention is better when you test yourself
while you study. Instead of reading a chapter twice, read it once, close the
book and try to remember as much as you can. (This is also known as the
<a href="http://www.scotthyoung.com/learnonsteroids/grab/TranscriptFeynman.pdf">Feynman Technique</a>.)</p>
<p>When rehearsing the material, <em>spaced repetition software</em> (SRS) such as
<a href="http://ankisrs.net">Anki</a> can be a powerful tool. The <a href="https://en.wikipedia.org/wiki/Forgetting_curve">forgetting curve</a>
hypothesis says that forgetting happens exponentially. Anki will test you
just ahead of this curve. Also, creating a deck of flashcards is a way to
process the material more deeply.</p>
<h2 id="learn-in-environment-similar-to-the-exam-hall">Learn in environment similar to the exam hall</h2>
<p>You recall information better when you’re in similar context as you were in
when learning the information. This includes the physical environment but also
your internal physiological state and mood.</p>
<p>This would suggest that you should study at the same time of the day as the
exam is. If you’re going to eat or drink coffee before exam, do it before
studying, too. Study in an environment that is similar to the examination hall.</p>
<h2 id="bonus-get-very-drunk-immediately-after-studying">Bonus: Get very drunk immediately after studying</h2>
<p>Consolidating new memory traces is a process that takes hours. Alcohol inhibits
consolidation of memory, so if you drink heavily, nothing will interfere with
consolidation of the material you’ve learned while studying. This means you
will remember the material more clearly.</p>
<p>I haven’t tried out this method myself, but hey, there’s research so it must be
true.</p>
NP and non-deterministic Turing machines
https://quanttype.net/p/np-and-ndtms/
Thu, 09 Oct 2014 00:00:00 +0000https://quanttype.net/p/np-and-ndtms/<p>The complexity class P is pretty straightforward: it’s the class of problems
that can be solved in polynomial time. What is NP, though?</p>
<p>I’w now attending <a href="http://www.helsinki.fi/~huuskone/Opetus/VT/">a course on computational complexity</a>. While I’ve
known about complexity classes for a long time, my understanding has been hazy.
I’ve known that the problems that can be solved in polynomial time belong to P
and harder problems belong to NP and thene there’s something beyond that. This
course has finally clarified to me what is NP.</p>
<p>The key to NP are <em>non-deterministic Turing machines</em> (NDTMs). They’re a
generalization of the ordinary, deterministic Turing machines: there can be
more than one rule for each state-symbol pair. This is the same as with
non-deterministic and deterministic finite automatons.</p>
<p>NDTM can be thought to run all the possible computations in parallel. A
possible intuition is that it “makes a copy of itself”, multiverse-style, when
it reaches a configuration where it can apply two or more rules and the
different copies apply different rules.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>NDTM accepts a language when there exists at least one computation (a series of
configurations of the machine) that finishes in an accepting state. It halts if
all the possible computations halt.</p>
<p>Formally, P is the complexity class of decision problems that can be solved in
polynomial time by a deterministic Turing machine. <em>NP is the complexity class
of decision problems that can be solved in a polynomial time by a
non-deterministic Turing machine.</em></p>
<p>It’s is sometimes said that NP is the class of problems whose solutions can be
checked efficiently (i.e. in polynomial time). Here’s how it works: construct a
NDTM that generates non-deterministically a solution candidate and then checks
it with that efficient procedure. This NDTM accepts the input if there’s at
least one computation that halts and hence it solves the decision problem
efficiently.</p>
<p>Consider the <a href="http://en.wikipedia.org/wiki/Partition_problem">partition problem</a>: you have a set of items with known
weights and two knapsacks and you want to split the items into the sacks so
that they weigh equally much. The decision problem asks, given the weights,
whether this can be done. A possible solution can be efficiently verified: if
you come up with a way to split up the items, you can easily sum the weights
and see if they match. This problem is indeed NP-complete: it can be solved
efficiently with a NDTM, but not with a deterministic Turing machine unless
P=NP.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This multiverse intuition is sometimes offered as an
explanation of what quantum computers do. I do not know much about
quantum computing, but I’m pretty sure this is wrong and quantum
computers are less powerful than NDTMs. <strong>Edit:</strong> See a
<a href="https://quanttype.net/posts/2015-03-21-are-quantum-computers-faster-than-classical.html">later post about this</a>. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Why does everything fall apart so quickly?
https://quanttype.net/p/why-does-everything-fall-apart-so-quickly/
Mon, 06 Oct 2014 00:00:00 +0000https://quanttype.net/p/why-does-everything-fall-apart-so-quickly/<p><a href="https://www.flickr.com/photos/arctan/14019335145/">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kengat_hu_ca3be551066bbe60.webp" type="image/webp" />
<img src="https://quanttype.net/images/kengat.jpg" width="600" height="450" alt="I used these shoes for a bit too long." loading="lazy" />
</picture>
</figure>
</a></p>
<p>I’ve lately thought about how much waste we produce. It’s mind-boggling. Every
time I put something in the garbage bin, I think <em>again</em>, and I doubt that our
household is amongst the worst producers of waste. Where did all that waste
come from? Especially all that plastic? What’s going to happen to it? Why all
my clothes wear out so quickly? Why all the electronic devices stop working so
quickly? Why can’t they be repaired?</p>
<p>In our apartment, we have a couple of chairs found from my girlfried’s
grandparents’ attic. The chairs can be seen in a photo from the 1920s, which
means they’re at least ninety years old, yet they’re in great condition. They
haven’t been in use all the time, but I’m still impressed: this kind of
durability is in stark contrast to the planned obsolescence of modern devices.</p>
<p>For people who like shopping and new stuff, and for the companies producing
that stuff, the short lifetime of their stuff is a blessing. If your clothes
quickly wear out, then you soon get to buy new ones. For me, it feels wasteful
and tedious.</p>
<h2 id="finding-durable-items">Finding durable items</h2>
<p>All this has made me pay more attention to the lifecycle of things around me.
Here are some things to think about:</p>
<ul>
<li>
<p>Will it age well? All items wear in usage. Some of them do it with dignity
(this is the aesthetic of <a href="http://en.wikipedia.org/wiki/Wabi-sabi">wabi-sabi</a>), while others take it less
well.</p>
</li>
<li>
<p>Can it be repaired? Can you do it yourself? Can you get spare parts? Wonders
can be done to clothes and furniture, but many electronic devices are not
designed to be user-serviceable. Still, there is a cool Ello post by Clay
Shirky about <a href="https://ello.co/cshirky/post/FQKBh4QMjfaqBM1C4XYnjw">hardware hackers in China</a>: in China, repairing
any electronics is business as usual.</p>
</li>
<li>
<p>Can it be resold or given away? Can it be recycled? When the item comes to
the end of its road, can its materials be re-used? Can they be safely
disposed?</p>
</li>
</ul>
Practicing photography
https://quanttype.net/p/practicing-photography/
Sat, 27 Sep 2014 00:00:00 +0000https://quanttype.net/p/practicing-photography/<p><a href="https://www.flickr.com/photos/arctan/15371770902/in/set-72157647570550377">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/pissaaja_hu_4d8b69196babc609.webp" type="image/webp" />
<img src="https://quanttype.net/images/pissaaja.jpg" width="600" height="450" loading="lazy" />
</picture>
</figure>
</a></p>
<p>I’ve been complaining that I take worse photos than, say, five years ago. A
couple of days ago I decided do something about it and start a small
photography project.</p>
<p><em>I’m very much a beginner when it comes to photography, so I’ll probably use
silly words and say things that are obvious or obviously wrong. Please bear
with me while I’m learning.</em></p>
<p><a href="https://www.flickr.com/photos/arctan/sets/72157647570550377"><strong>Click here to see my progress on Flickr.</strong></a></p>
<p>I take most of my photographs when I travel. I take them to better remember the
places I’ve been to and to better describe my adventures to the friends and family.
I’d like to proudly show my pictures, but instead I’m going <em>ugh, this is so
bad</em>.</p>
<p>To get better, for the next 28 days, I’m going to publish one 4:3 black-white
urban landscape shot taken with my point-and-shoot on that day. The photos will
be <a href="https://www.flickr.com/photos/arctan/sets/72157647570550377">on Flickr</a>. This may sound somewhat arbitrary. I could come up with
some justifications for these choices, but to be honest, the idea just popped
into my head and why not?</p>
<p>There’s only so much you can learn with such a small project. My goal is to
make myself think about the photos I take, and to get a basic idea on how to
take a urban landscape shot. Also I hope to take at least one shot I can be
proud of.</p>
<h2 id="initial-insights">Initial insights</h2>
<p>I’ve now been doing this for four days. Here’s what I’ve figured out so far.</p>
<p>One thing I quickly realized is that it’s much easier to take an agreeable
photo of a detail (e.g. an interesting ornament) than it is to take a photo of
a whole (e.g. a stretch of street). This also helped me understand why my older
photos were better. I’ve assumed it’s because I practiced more, which is true,
but it is also because I took mostly photos of details.</p>
<p>During the last few years I’ve shifted to trying to capture wholes. While
detail pictures are fun and important, they feel very detached if you don’t
have pictures of the whole to tie them together.</p>
<p>So, capturing the whole is what I’m now interested in. How to take a photo that
gives you a feeling of the city? I might be even doing the wrong thing: instead
of concentrating on single shots, should I concentrate on series of images?</p>
<p>Another hunch is that a very important feature of the urban landscape is its
shape in three dimensions. The city is not just a painting on a plane and you
will fail if you try to capture it like that. One way to make an interesting
image is to capture the shape of the space.</p>
<p><strong>Update (2014-10-23):</strong> <a href="https://quanttype.net/posts/2014-10-23-photo-practice-retro.html">Read here what happened!</a></p>
Houkutteleva tutkijanura
https://quanttype.net/p/tutkijanura/
Fri, 26 Sep 2014 00:00:00 +0000https://quanttype.net/p/tutkijanura/
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/kaisatalo_hu_920da24a1afa2466.webp" type="image/webp" />
<img src="https://quanttype.net/images/kaisatalo.jpg" width="600" height="450" alt="Kaisa-talossa pyörii niitä pahamaineisia opiskelijoita." loading="lazy" />
</picture>
</figure>
<p><em>Briefly in English: This is a post about I think doing research would be cool,
but the career options in (Finnish) universities seem atrocious. Because the
articles I refer to are in Finnish, I wrote this in Finnish, too.</em></p>
<p>Välillä olen miettinyt, että pitäisikö suunnata urani käytännön
ohjelmistokehityksen sijaan tutkimuksen saralle. Esimerkiksi ohjelmointikielten
tutkimus on ala, jota seuraan mieleenkiinnolla ja johon syventyisin mielelläni.</p>
<p>Onneksi yliopistoväki on muistuttanut tutkija-uran ongelmista. Esimerkiksi
<a href="http://jatulintarha.wordpress.com/2014/09/12/kohututkijan-avoin-tilitys-seksivau/">Jenny Kangasvuon mukaan</a> jatko-opinnot polttavat ihmisen loppuun
ja yliopistolta ei tukea heru. <a href="http://www.hs.fi/tiede/a1410057794427?jako=4e1f87147d86138f61702b50018b5cb7">Janne Saarikiven</a> ja
<a href="http://tiedemies.blogspot.fi/2014/09/ajolahto-reunahuomautus.html">Tiedemiehen</a> mukaan tutkijat ja varsinkaan professorit eivät ehdi
tehdä tutkimusta, sillä kaikki aika kuluu hallinnollisiin tehtäviin ja
rahoituksen hankkimiseen. Eilen puolestaan sai lukea siitä, kuinka <a href="http://www.hs.fi/kaupunki/Irtisanotut+syytt%C3%A4v%C3%A4t+Helsingin+yliopistoa+n%C3%B6yryytt%C3%A4misest%C3%A4/a1411654713272">Helsingin
yliopisto hoitaa irtisanomiset</a>. Ei tainnut mennä kovin hyvin.</p>
<p>Kun tällaisia kirjoituksia lukee, valinta ei ole vaikea. Akateeminen vapaus on
siisti juttu, mutta myös se on siistiä, että IT-alalla koodarit laitetaan
koodaamaan, työsopimukset tehdään toistaiseksi ja palkkaakin maksetaan. Ainakin
tällä hetkellä.</p>
In praise of Hiccup
https://quanttype.net/p/in-praise-of-hiccup/
Thu, 25 Sep 2014 00:00:00 +0000https://quanttype.net/p/in-praise-of-hiccup/<p>I’m writing a simple CRUD web application for a school course and I’m doing it
in Clojure. I chose to do the templating with
<a href="https://github.com/weavejester/hiccup">Hiccup</a>. It’s a Clojure library for
generating HTML. You specify the structure as inline data, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">hiccup.page</span> <span class="ss">:as</span> <span class="nv">h</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">example-page</span> <span class="p">[</span><span class="nv">title</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">h/html5</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:head</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:title</span> <span class="nv">title</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:body</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:div.container</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:h1</span> <span class="nv">title</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="ss">:p</span> <span class="s">"Hello world!"</span><span class="p">]]]))</span>
</span></span></code></pre></div><p>For a long time, there has been a debate how much or little logic you should
have in your templates. On the one end of spectrum is
<a href="http://mustache.github.io">Mustache</a>, which advertises itself as <em>logic-less</em>.
On the other end is PHP, where all you have are templates and programs are
fully embedded in them.</p>
<p>I’m unsure which one is the right way, but I do say this: if you choose to
embed logic in your templates, you should use a proper programming language.
This is what Hiccup does: Hiccup templates are just Clojure code that generates
plain old Clojure data. Contrast this to e.g. Django’s templating language,
which allows you to do a lot of things, but never quite as much as you’d want.</p>
Is being normal anti-establishment?
https://quanttype.net/p/being-normal/
Fri, 12 Sep 2014 00:00:00 +0000https://quanttype.net/p/being-normal/<p>The more alike to others you are, the more you need to be surveilled to
characterise you properly. Does that make aiming to be normal an act against
the surveillance establishment?</p>
Electromagnetic Field 2014
https://quanttype.net/p/emf2014/
Sun, 07 Sep 2014 00:00:00 +0000https://quanttype.net/p/emf2014/<p><a href="https://www.flickr.com/photos/arctan/15166765625/">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/emf_hu_17a9d7639b491965.webp" type="image/webp" />
<img src="https://quanttype.net/images/emf.jpg" width="600" height="337.5" loading="lazy" />
</picture>
</figure>
</a></p>
<p>Last weekend I went to <a href="http://emfcamp.org/">Electromagnetic Field 2014</a> in Bletchley, UK. EMF
is a hackercamp - there were over 1000 makers and hackers camping on a field.
You could get electricity to your tent, and there was fast Internet connection.</p>
<p>It was a pretty cool event. I met some nice people, attended some cool talks,
and generally had a good time. The Arduino Duo -compatible event badge, <a href="https://wiki.emfcamp.org/wiki/TiLDA_MKe">TiLDA
MKe</a>, was cool too, even though the radio network never really worked.</p>
<p>There were several really good talks. Here are my favorites:</p>
<ul>
<li>
<p><em>Walt Disney World: This was supposed to be the future</em> by <em>Dan W</em>, was a
talk about the history of <a href="http://en.wikipedia.org/wiki/Epcot">Epcot</a>, which is part of Disney World
in Florida. Turns out it was Walt Disney’s attempt at utopian city planning.
A fascinating story.</p>
</li>
<li>
<p><em>Where Games Break</em> by game designer <em>Hannah Nicklin</em> was a personal talk
about how and where various games (understood very broadly) break. It was
gripping and poetic talk, more like a performance than a lecture.
Unfortunately I can’t really summarize it in words. <a href="http://www.hannahnicklin.com/2014/07/where-games-break/">Text of the talk
here</a>.</p>
</li>
<li>
<p><em>bach.js, an unhistory of how the great Baroque composer pioneered
Javascript, 250 years before Netscape even existed</em> by <em>James Aylett</em> was a
wonderfully silly talk about the exploits of Johann Sebastian Bach as an
early JavaScript programmer. I’m not sure if it made much sense, and half of
the jokes went over my head because I don’t know much about Bach, but hey.</p>
</li>
<li>
<p><em>Surreal Numbers And Mathematical Games</em> by <em>Tom Hall</em> was a talk about how
all of the certain kind of games are actually games of <a href="http://en.wikipedia.org/wiki/Nim">Nim</a>. Even
though it was fast-paced, Tom ran out of time. It was so fast-paced that I’m
not sure if anyone in the audience was able to really follow the talk, unless
they had previous knowledge of combinatorial game theory. <a href="https://docs.google.com/presentation/d/16vTWDHBplrFDwndqzTURAV0s7OoAEy9KR58dTh8WXVY/edit">Slides
here</a>.</p>
</li>
</ul>
<p>I usually don’t get much out of talks in this kind of events, because I’m
superficially familiar with a lot of stuff and the speakers tend to not go very
deep. Probably one of the reasons that these talks stood out was that I was new
to all of the topics and couldn’t properly follow the talks.</p>
<p>All in all, it was a great weekend and I will certainly consider coming back for
EMF 2016.</p>
Math is programming
https://quanttype.net/p/math-is-programming/
Tue, 05 Aug 2014 00:00:00 +0000https://quanttype.net/p/math-is-programming/<p>Lately the people of the Internet have been concerned about whether programming
is math. Sarah Mei has written a good post about this: <a href="http://www.sarahmei.com/blog/2014/07/15/programming-is-not-math/">Programming Is Not
Math</a>. Here I want to record my internal dialogue on the matter.</p>
<hr/>
<p><strong>Is programming math?</strong></p>
<p>Yes.</p>
<p><strong>Why?</strong></p>
<p>Because programming and math are so deeply interlinked that it’s impossible to
distinguish them in their core. For example, have a look at type theory. Does
it describe mathematics? Yes. Does it describe programming? Yes.</p>
<p><strong>Is that the only reason?</strong></p>
<p>No. <em>Doing</em> programming and <em>doing</em> mathematics feel the same to me. Thinking
about proofs feels like debugging programs. Exploring mathematics feels
like exploratory programming.</p>
<p><strong>Do I need to be good at math to be a good programmer?</strong></p>
<p>Yes. Programming is math, so you can’t be good at programming if you aren’t
good at math.</p>
<p><strong>Do I need to learn math to learn programming?</strong></p>
<p>Yes. Programming is math, so you need to learn math.</p>
<p><strong>What kind of math should I learn to learn program?</strong></p>
<p>Since programming is math, your best bet is to start by learning programming.</p>
<p><strong>What if I’m bad at math? Can I still learn to program?</strong></p>
<p>When you say you’re bad at math, you probably mean you didn’t do too well in
the school math class. Programming is math, but luckily it’s very different
math from what they teach at schools. You should give it a try, you might be
surprised by how good you’re at math.</p>
<p><strong>Do you agree with Sarah Mei?</strong></p>
<p>Yes, even though programming is math.</p>
<p><strong>Does it matter whether programming is math?</strong></p>
<p>Not at all. Math is programming, though, and that is interesting.</p>
When is static typing worthwhile?
https://quanttype.net/p/when-is-static-typing-worthwhile/
Tue, 24 Jun 2014 00:00:00 +0000https://quanttype.net/p/when-is-static-typing-worthwhile/<p>Static typing is a crucial tool for constructing correct programs. That does not mean
you should always use a statically typed programming language. Often program
correctness does not matter and “mostly works” is good enough. you might be
better off with a dynamic programming language that has good libraries for
solving your problem.</p>
<p>When is static typing worthwhile, then? I have a couple of hunches:</p>
<ol>
<li>
<p><em>The value of types increases over time.</em>
They make it easier to understand large codebases and they ensure that your
changes are correct.</p>
</li>
<li>
<p><em>The value of types increases together with problem complexity.</em>
If the problem is complex, the extra structure given by static typing allows
you to concentrate on the problem itself instead of worrying whether you’ve
constructed a correct program.</p>
</li>
<li>
<p><em>Types boost you in the core of the program, but hinder on the edges.</em>
Often you need to interface with the untyped world, e.g. via JSON-speaking
APIs. Marshalling the data back and forth takes extra effort in the
statically typed world.</p>
</li>
</ol>
Hard things in software engineering
https://quanttype.net/p/hard-problems-in-software-engineering/
Sun, 04 May 2014 00:00:00 +0000https://quanttype.net/p/hard-problems-in-software-engineering/<p>According to common wisdom, there are only <a href="http://martinfowler.com/bliki/TwoHardThings.html">two hard things</a>
in computer science: cache invalidation, naming things and off-by-one errors.</p>
<p>In similar vein, here’s my list of hard things in software engineering (not in
order):</p>
<ul>
<li>security</li>
<li>concurrency</li>
<li>dealing with people</li>
</ul>
<p>What makes them hard is that when solving a security/concurrency/people
problem, the difference between a good solution and a catastrophically bad one
is often subtle. All of these are commonly seen as much more simple than they
are.</p>
Linux problems, April 2014
https://quanttype.net/p/linux-problems/
Sun, 06 Apr 2014 00:00:00 +0000https://quanttype.net/p/linux-problems/<p>At work I use Linux, or to be more precise, Ubuntu 12.04. I’ve had all kinds of
problems with it and I thought that it’d be nice to track them by making a
list. I’ve included the things that I’d really expect to just work by this
point.</p>
<h2 id="recently-solved-problems">Recently solved problems</h2>
<ul>
<li>
<p>Firefox menus stopped working. When you started the browser, everything would
work just fine, but after a while the menus and drop-downs would stop
opening. I think it is <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=779900">a known bug</a> in Firefox. It disappeared a
while ago, maybe after some software update. <em>Update 2014-04-18: It’s happening again. :(</em></p>
</li>
<li>
<p>Chrome tabs would black out. Some pages, typically ones with Flash or some
other multimedia content, would suddenly urn totally black. Sometimes when
you opened the page again in a new tab, it would work, sometimes not. I think
it had something to with nVidia drivers. The problem disappeared after
upgrading the drivers and rebooting the computer.</p>
</li>
<li>
<p>nvidia-settings crashes when you try to apply settings. It still does, but it
turns out that the <em>Save to X Configuration File</em> button actually works and
you just need to restart the X server after making changes. No more
hand-editing Xorg.conf.</p>
</li>
</ul>
<h2 id="current-problems">Current problems</h2>
<ul>
<li>
<p>Whenever I plug in my USB DAC (i.e. soundcard), I need to restart Spotify to
hear audio again. It used to work without restarts, but maybe some software
update broke it. (The said DAC, NuForce uDAC-2 occasionally overheats, so I
unplug it when I leave the office.)</p>
</li>
<li>
<p>Sometimes, like every time I plug in a USB device, the X keyboard layout gets
reset. I use xmodmap to set up a custom layout and this undoes all those
customizations. My boss points out that instead of using xmodmap, I should
create a proper XKB layout. I guess it would help, but I’ve never encountered
this problem with xmodmap elsewhere.</p>
</li>
</ul>
Baudrillard on Jogging
https://quanttype.net/p/baudrillard-on-jogging/
Sat, 05 Apr 2014 00:00:00 +0000https://quanttype.net/p/baudrillard-on-jogging/<p>One of the books I’m reading right now is Jean Baudrillard’s <em>The Transparency
of Evil</em>. It’s a collection of essays. In one of them, <em>Operational Whitewash</em>,
he writes:</p>
<blockquote>
<p>Jogging is another activity in the thrall of the performance principle. […]
The pleasure (or pain) of jogging has nothing to do either with sport or with
the body in its fleshly reality: it is the pleasure not of pure physical
exertion but of of a dematerialization, of an endless functioning. The body
of the jogger is like one of Tinguely’s machine: ascesis and ecstasis of the
performance principle. Making the body run soon gives way, moreover, to
letting the body run: the body is hypnotized by its own performance and goes
on running on its own, in the absence of a subject, like somnambulist and
celibate machine.</p>
</blockquote>
<p>I’m not sure if understand what he is saying, but the comparison to Tinguely’s
machines is apt. Consider Hannibal II:</p>
<div style="text-align: center"><iframe src="//instagram.com/p/lu2uFcCMFL/embed/" width="612" height="710" frameborder="0" scrolling="no" allowtransparency="true"></iframe></div>
<p>I can’t make my mind about the book. Sometimes I think that Baudrillard is just
putting words after words without saying anything. Sometimes I feel like he’s
doing an excellent analysis of people project a perfected image of themselves
in social media – even though the book was published in 1990. <a href="https://quanttype.net/posts/2014-01-03-people-never-change.html">People never
change</a>, I guess.</p>
Renaming files with zmv
https://quanttype.net/p/renaming-with-zmv/
Sun, 09 Mar 2014 00:00:00 +0000https://quanttype.net/p/renaming-with-zmv/<p>Sometimes you want to rename a bunch of files. For example, maybe you want to
rename all the <code>.htm</code> files in the directory to <code>.html</code>. Previously I’d give a
shell command like this:</p>
<pre tabindex="0"><code>for f in *.htm; do
mv $f $(basename $f .htm).html
done
</code></pre><p>It works, but it’s cumbersome. There are better tools available. For example,
Perl comes with <code>rename</code>, which lets you do this:</p>
<pre tabindex="0"><code>rename 's/\.htm$/.html/' *.htm
</code></pre><p>Another tool, and the one I’ve been using lately, is <code>zmv</code>, which is a part of
Zsh. The <code>.htm</code> renaming goes like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">zmv <span class="s1">'(*).htm'</span> <span class="s1">'$1.html'</span>
</span></span></code></pre></div><p>As a more advanced example, recently I wanted to rename all the subdirectories
of a directory to uppercase. This was inside a git repo, so I needed to use
<code>git mv</code> instead of plain <code>mv</code>. Here’s how I did it:</p>
<pre tabindex="0"><code>zmv -Q -i -p 'git' -o 'mv' '(*)(/)' '${(U)1}'
</code></pre><ul>
<li><code>-Q</code> turns on bare glob qualifiers. This is required for <code>(/)</code>, which makes
<code>*</code> match only directories.</li>
<li><code>-i</code> enables interactive mode, as I wanted to manually confirm each renaming.</li>
<li><code>-p</code> specfifies the program and <code>-o</code> the arguments for it.</li>
</ul>
<p>To use <code>zmv</code>, you need to load it with <code>autoload -U zmv</code>. See <a href="http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#index-zmv"><code>man zshcontrib</code></a> for documentation. I also highly recommend skimming the
<a href="http://zsh.sourceforge.net/Doc/Release/Expansion.html#Expansion">Zsh manual on expansion</a> (<code>man zshexpn</code>) – it’s a treasure trove
that keeps giving.</p>
Weak views, strongly held
https://quanttype.net/p/weaks-views-strongly-held/
Sat, 01 Mar 2014 00:00:00 +0000https://quanttype.net/p/weaks-views-strongly-held/<p>A couple of days ago I read a fascinating essay. It’s Venkatesh Rao’s post <a href="http://www.ribbonfarm.com/2014/02/20/the-cactus-and-the-weasel/">The
Cactus and The Weasel</a>. He starts from the archetypes of fox and
hedgehog, originally <a href="http://en.wikipedia.org/wiki/The_Hedgehog_and_the_Fox">introduced by Isaiah Berlin</a>, and goes on to
analyze their thinking in detail. I attempted to summarize it, but failed. Just
read it yourself.</p>
<p>I’m certainly more of a fox than a hedgehog.</p>
Flappy Sine
https://quanttype.net/p/flappy-sine/
Sat, 15 Feb 2014 00:00:00 +0000https://quanttype.net/p/flappy-sine/
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/flappy-sine.png" width="600" height="360.81081081081084" loading="lazy" />
</picture>
</figure>
<p><a href="http://vapaus.org/flappy-sine/">My tribute to Flappy Bird</a>. It’s not quite as hard as Flappy Bird, though.</p>
nix-docker and docker volumes
https://quanttype.net/p/nix-docker-volumes/
Mon, 03 Feb 2014 00:00:00 +0000https://quanttype.net/p/nix-docker-volumes/<p><a href="https://github.com/zefhemel/nix-docker">nix-docker</a> is a tool by Zef Hemel that allows you to provision
your Docker images using <a href="http://nixos.org/">Nix</a>. If that sounds like a good idea, head over
to Zef’s blog to read his post <a href="http://zef.me/6049/nix-docker">Declaratively Provision Docker Images Using
Nix</a>. He makes a compelling case.</p>
<p>Over the weekend I experimented a bit with nix-docker. One thing that took me a
while to figure out is that if you want to mount a <a href="http://docs.docker.io/en/latest/use/working_with_volumes/">data
volume</a>, the mount point must exist on the image.</p>
<p>Zef’s <a href="https://github.com/zefhemel/nix-docker/blob/master/samples/wordpress.nix">Wordpress example</a> uses a volume mounted at <code>/data</code>. It
works, because the Apache service specification takes care of creating the
directory. If you want to create the directory yourself, you can add a script
to <code>docker.buildScripts</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">...</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">docker</span><span class="o">.</span><span class="n">volumes</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"/data"</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="n">docker</span><span class="o">.</span><span class="n">buildScripts</span><span class="o">.</span><span class="n">createVolumeDirs</span> <span class="o">=</span> <span class="s1">''
</span></span></span><span class="line"><span class="cl"><span class="s1"> mkdir -p /data
</span></span></span><span class="line"><span class="cl"><span class="s1"> ''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This could be and should be abstracted away, but I’m very much a beginner with
Nix. Or maybe nix-docker should take care of creating the directories.</p>
Baana, Helsinki
https://quanttype.net/p/baana/
Tue, 28 Jan 2014 00:00:00 +0000https://quanttype.net/p/baana/<p><a href="http://www.flickr.com/photos/arctan/12089917664/">
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/baana_hu_b8d351ff25de960f.webp" type="image/webp" />
<img src="https://quanttype.net/images/baana.jpg" width="600" height="337.43382908999246" loading="lazy" />
</picture>
</figure>
</a></p>
Compiling assembler files with avr-gcc without C runtime
https://quanttype.net/p/avr-gcc-assembler/
Mon, 27 Jan 2014 00:00:00 +0000https://quanttype.net/p/avr-gcc-assembler/<p>Written down so that I’ll remember this the next time I’m trying to do this: If
you want to compile assembler files with avr-gcc to be used with your C files,
just compile them like your <code>.c</code> files.</p>
<pre><code>avr-gcc -c -mmcu=atmega328p -o foo.o foo.S
</code></pre>
<p>If you want to run your assembler code standalone, without the C runtime
support (<code>crt*.o</code>), you’ll need to tell the linker that the entry point is
inside your program instead of the C initialization routines. The command-line
option <code>-e</code> for avr-ld is what you’re looking for. Consider the following
assembler program:</p>
<pre tabindex="0"><code> #include <avr/io.h>
.section text
.org 0
.global init
init:
rjmp main
.org 0x020
.global main
main:
; on Arduino, this will light up the on-board LED
ldi 16, 0xFF
out _SFR_IO_ADDR(DDRB), 16
out _SFR_IO_ADDR(PORTB), 16
loop:
rjmp loop
</code></pre><p>To start the execution from <code>init</code>, compile and link it like this:</p>
<pre><code>avr-gcc -c -mmcu=atmega328p -o foo.o foo.S
avr-ld -e init -o foo.elf foo.o
avr-objcopy -O ihex foo.hex foo.elf
</code></pre>
<p>Also, check out <a href="http://www.nongnu.org/avr-libc/user-manual/assembler.html">avr-libc’s manual</a> on assembler programs.</p>
People Never Change
https://quanttype.net/p/people-never-change/
Fri, 03 Jan 2014 00:00:00 +0000https://quanttype.net/p/people-never-change/<p>For ages thinkers have been lamenting the common people for their lack of
passion in their lives. For example, here’s a quote from Søren Kierkegaard’s
<em>Concluding Unscientific Postscript</em> (originally published in 1846, translation
by Alistair Hannay):</p>
<blockquote>
<p>Every human being is fitted by nature to become a thinker (all honour and
praise to the God who created man in his image!). God cannot help it if habit
and routine and want of passion, and affectation, and gossiping with
neighbours next door and opposite little by little ruin most people so that
they become thoughtless – and base their eternal happiness on one thing and
then another and then something else – not noticing the secret that their
talk about their eternal happiness is an affectation precisely because it is
devoid of passion, which is why it can also be so excellently supported by
matchstick arguments.</p>
</blockquote>
<p>I encountered this quote when reading Torsti Lehtinen’s book
<a href="http://arktinenbanaani.fi/tuote/torsti_lehtinen/eksistentialismi_vapauden_filosofia/9789522700452">Eksistentialismi</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It’s an overview of existentialism
and the main thinkers behind it. All the existentialists found despicable the
complacent, petit bourgeois mode of existence. While I don’t have any quotes
ready, I’m sure that these thoughts aren’t new. Surely you can find ancient
Greeks saying similar things.</p>
<p>This makes me wonder: have there been any philosophers who thought the
opposite? Maybe humans aren’t created to be thinkers - maybe the human nature
is to be petit bourgeois?</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>If you’re interested in the subject and you’re able to read Finnish, I
highly recommend the book. It was a great introductory read. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Peruna, a horse and a medicine
https://quanttype.net/p/peruna/
Mon, 25 Nov 2013 00:00:00 +0000https://quanttype.net/p/peruna/<p>The mascot of the athletic team of Souther Methodist University is a black
shetland pony called Peruna. Peruna has been their mascot since 1932 - the
current Peruna is the ninth in the succession. Here’s Peruna III:</p>
<figure class="hero">
<picture>
<source srcset="https://quanttype.net/images/peruna3x_hu_1d8f2bff11f4901e.webp" type="image/webp" />
<img src="https://quanttype.net/images/peruna3x.jpg" width="600" height="747" loading="lazy" />
</picture>
</figure>
<p>Peruna got its name from a patent medicine that was sold in the turn of the
century. The temperance movement in the US was strong and alcohol dealers faced
all kinds of hindrances. Still, many medicines with alcohol were available
even in states under prohibition, and they were very popular. Their medical
effects were dubious but at least they made you drunk.</p>
<p>What was in Peruna the medicine? According to <a href="http://www.bottlebooks.com/Peruna_reprinted_from_collier.htm">these
reprints</a>, Collier’s Weekly gave the following recipe for
home-made Peruna:</p>
<ul>
<li>Half a pint of 190-proof (95 vol-%) cologne spirits</li>
<li>A pint and a half of water</li>
<li>A little cubebs for flavor</li>
<li>A little burned sugar for color</li>
</ul>
<p>There’s also a thorough article by Jack Sullivan on the history of Peruna in
<a href="http://www.fohbc.org/PDF_Files/Peruna_JSullivan.pdf">the May-June 2007 edition</a> of Bottles and Extras (the official
publication of the Federation of Historical Bottle Collectors)</p>
<p>Where did the medicine get its name? I don’t know. Peruna means potato in
Finnish and I was hoping to learn that the medicine was made of potato alcohol.
Maybe Dr. Samuel Brubaker Hartman, the inventor of Peruna, was a Finnish
American or something, I thought. Unfortunately that does not seem to be the
case.</p>
<p class="credits">Photo: [Peruna III bucking][peruna-photo] / Southern
Methodist University, Central University Libraries, DeGolyer Library.</p>
Mental Attitude of the Grid
https://quanttype.net/p/responsible-grid/
Mon, 18 Nov 2013 00:00:00 +0000https://quanttype.net/p/responsible-grid/<p>The typographic grid is a graphic designer’s tool for structuring the page. You
split the page into a grid and use that as a guide for laying out the design
elements. <a href="http://en.wikipedia.org/wiki/Grid_(graphic_design)">Wikipedia writes</a>:</p>
<blockquote>
<p>The grid serves as an armature on which a designer can organize graphic
elements (images, glyphs, paragraphs) in a rational, easy to absorb manner.</p>
</blockquote>
<p>The word <em>rational</em> is telling. The grid was born during the 20th century
inspired by the modernist ideals. It’s not just a practical tool, but a
principled one. The definitive book on the grid is Josef Müller-Brockmann’s
<em>Grid Systems in Graphic Design</em>. He lists the reasons to use a grid:</p>
<blockquote>
<ul>
<li>economic reasons: a problem can be solved in less time and at lower cost.</li>
<li>rational reasons: both simple and complex problems can be solved in a
uniform and characteristic style.</li>
<li>mental attitude: the systematic presentation of facts, of sequences of
events, and of solutions to problems should, for social and educational
reasons, be a constructive contribution to the cultural state of society
and an expression of our sense of responsibility.</li>
</ul>
</blockquote>
<p>A graphic designer is someone who solves problems. The problem they’re solving
is how to best communicate visually something. I’ve never seen it put this way
on the Internet, but it seems like a very valid way of viewing the profession.</p>
<p>How do they solve the problems? Efficiently, of course, both visually and
economically. But they also solve them responsibly and for the betterment of
the society.</p>
<p>I was surprised to encounter a call for responsible design in a book on grid
systems, but it makes sense. There’s a great talk by Mike Monteiro along
similar lines, titled <a href="https://vimeo.com/68470326">How Designers Destroyed the World</a>. To
summarize it:</p>
<blockquote>
<p>You are directly responsible for what you put into the world.</p>
</blockquote>
<p>The talk is not just for designers, but for everyone who works on products. You
could do worse than watch it.</p>
Silver Streetcar for the Orchestra
https://quanttype.net/p/silver-streetcar/
Sat, 16 Nov 2013 00:00:00 +0000https://quanttype.net/p/silver-streetcar/<figure class="youtube"><iframe width="640" height="360" src="//www.youtube-nocookie.com/embed/nRFZDDCL-M8?rel=0" frameborder="0" allowfullscreen></iframe></figure>
<p>A colleague pointed me to <em>Silver Streetcar for the Orchestra</em> by Alvin Lucier.
Here is one of the performances available on the Internet.</p>
<p>The solo triangle player dampens the triangle with their hand while playing a
fast rhytm. They’re exploring how the dampening and the location, speed and
loudness of the tapping affect the sound of the triangle. To quote <a href="http://www.dramonline.org/albums/alvin-lucier-ever-present/notes">Alvin
Lucier</a>:</p>
<blockquote>
<p>During the course of the performance, the acoustic characteristics of
the folded metal bar are revealed.</p>
</blockquote>
<p>I find it wonderful how so rich musical work can come out of so constrained
setup. This piece keeps playing in my head.</p>
<p>The name of the piece comes from Luis Buñuel’s surrealist piece of writing
<a href="http://books.google.fi/books?id=tK3KPgv34U8C&lpg=PA8&vq=silver%20streetcar&pg=PA6#v=onepage&q&f=false"><em>Orchestration</em></a>, which describes the roles of different
instruments in an orchestra. <em>A silver streetcar for the orchestra</em> is of
course the triangle. It’s a worthy read.</p>
ferret.gif
https://quanttype.net/p/ferret/
Fri, 23 Aug 2013 00:00:00 +0000https://quanttype.net/p/ferret/
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/fretti.gif" width="600" height="337.39130434782606" loading="lazy" />
</picture>
</figure>
Why do you write commit messages?
https://quanttype.net/p/why-do-you-write-commit-messages/
Tue, 20 Aug 2013 00:00:00 +0000https://quanttype.net/p/why-do-you-write-commit-messages/<p>If you write commit messages, take a couple of minutes to consider the
following questions:</p>
<ul>
<li>Who reads your commit messages?</li>
<li>When do they read them?</li>
<li>Why do they read them?</li>
</ul>
<p>Your answers likely depend on the kind of projects you’re working on. For my
personal projects, the answers to the first two are “only me” and “never”. As
a result, my commit messages are mostly one-liners along the lines of <code>fix everything</code>. That’s fine, because my projects are one-man short-term
operations.</p>
<p>At <a href="http://www.zenrobotics.com/">work</a>, the situation is different. We have around twenty persons
working on a codebase with over 150k lines of code and years of history. I
can’t tell when (or if ever), my co-workers read my commit messages, but I do
know when I read theirs.</p>
<p>The first time I read the messages is when the code is in review. I like to get
quick overview of the changes by looking at the commit messages of the change
request. However, it’s not a big deal if the messages don’t tell much - the
main concern is the code itself.</p>
<p>The second time is more important: it’s when I try to understand the purpose of
some old code. This often takes me digging through all the commits that have
touched the code. Ideally the code would be self-documenting and there would be
an explanatory comment next to it. In practice, the code and the comments get
obsolete and out-of-sync. The code might be written or modified by a person who
does not fully understand it. A wide-ranging change can miss something.
Something obvious to the original author might not be obvious two years later.
Sometimes you just need to make an awful hack.</p>
<p>In these cases the original commits introducing the code often helps me
understand what is happening. It’s even better when there’s a commit message
documenting the thinking behind the change: What problem does this commit
solve? Why was it solved this way? Why wasn’t some other way used?</p>
<p>I’ve worked at ZenRobotics for only eight months, so my code hasn’t yet had to
take the test of time. Nevertheless I’ve started to make it more future-proof
by writing more extensive commit messages. Hopefully in two years I’ll be
thanking myself for doing it.</p>
<p>I hope these questions help you figure out what kind of commit messages are
useful for you and your contributors.</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li>Tim Pope has <a href="http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html">recommendations</a> for formatting the commits.</li>
<li>John Graham-Cumming explains how his commit messages have <a href="http://blog.jgc.org/2013/07/write-good-commit-messages.html">become more
verbose</a>.</li>
</ul>
Setting up nginx for static content with Pallet
https://quanttype.net/p/pallet-and-nginx/
Sun, 18 Aug 2013 00:00:00 +0000https://quanttype.net/p/pallet-and-nginx/<p>In this post I’ll show you how I used <a href="http://palletops.com/">Pallet</a> to configure
the server hosting this blog.</p>
<h2 id="what-and-why">What and why?</h2>
<p>I’m setting up a web server to host a bunch of static HTML, CSS and image
files. That’s a pretty simple task, so what is Pallet and why am I using it?</p>
<p>I want to have reproducible server configuration. If I ever need to move
quanttype.net to a new server, I do not want to figure out which packages to
install and which hand-edited configuration files I need to copy over. Various
configuration management tools such as this problem by programmatically
applying my configuration to a server.</p>
<p>I’ve earlier dabbled with some of more popular configuration management tools
such as <a href="https://puppetlabs.com/">Puppet</a> and <a href="http://wiki.opscode.com/display/chef/Home">Chef</a>. This time I chose to use
<a href="http://palletops.com/">Pallet</a> because it’s simpler and hence suits my simple needs better.</p>
<h2 id="pallet-cloud-automation-with-clojure">Pallet: cloud automation with Clojure</h2>
<p><a href="http://palletops.com/">Pallet</a> is a Clojure library that can be used to describe a server
configuration and then apply it to a server. It’s simple to use: all you need
is a Clojure REPL on your local computer and SSH connection to your servers.
Pallet also suppert automatically spinning up nodes with cloud providers such
as Amazon or Rackspace.</p>
<p>Pallet’s abstraction level is somewhere between those of <a href="https://puppetlabs.com/">Puppet</a> and
<a href="http://fabfile.org/">Fabric</a>. You can easily use pre-made modules (“crates” in Pallet
parlance) for installing and configuring common software like nginx, but you do
not need to set up any central servers or repositories.</p>
<h2 id="acquiring-a-server">Acquiring a server</h2>
<p>Quanttype is hosted on <a href="https://www.digitalocean.com/">DigitalOcean</a>, mainly because their
prices are cheap. Pallet does not support their API, so I manually created a
Ubuntu VM, or a droplet as DigitalOcean calls them. Before using Pallet I did
some setup on the server:</p>
<ol>
<li>
<p>Create a non-root user with passwordless sudo access. This is not mandatory:
you could use Pallet as root as well, but this the way I like to organize
the things.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># as root</span>
</span></span><span class="line"><span class="cl">adduser arcatan
</span></span><span class="line"><span class="cl">visudo
</span></span></code></pre></div></li>
<li>
<p>Install your SSH key. Pallet uses SSH to connect to the server and you don’t
want to store your password in your Pallet configuration.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># on your own computer</span>
</span></span><span class="line"><span class="cl">ssh-copy-id quanttype.net
</span></span></code></pre></div></li>
</ol>
<h2 id="a-new-pallet-project">A new Pallet project</h2>
<p>Pallet’s <a href="http://palletops.com/doc/first-steps/">getting started guide</a>
suggests to use Leiningen to create a new project with <code>pallet</code> template. I did
so, but I didn’t know what to do with all the stuff Leiningen put there, so I
revereted back to a plain Clojure project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">lein init quanttype-ops
</span></span></code></pre></div><p>Frankly, I had a hard time following Pallet’s documentation on this. It covers
okay how you do various things, but it’s lacking on how to structure your
Pallet project.</p>
<p>The stable release of Pallet is 0.7, but 0.8 is going to be released soon. The
first release candidate is out there already, so it’s probably a good idea to
base new projects on that.</p>
<p>Pallet is distributed as a Clojure library, so I added <code>[com.palletops/pallet "0.8.0-RC.1"]</code> to project.clj. I’m also going to use nginx-crate, so I’ll add
that, too.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defproject </span><span class="nv">quanttype-ops</span> <span class="s">"0.1.0-SNAPSHOT"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:description</span> <span class="s">"Pallet for quanttype.net"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:dependencies</span> <span class="p">[[</span><span class="nv">org.clojure/clojure</span> <span class="s">"1.5.1"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">com.palletops/pallet</span> <span class="s">"0.8.0-RC.1"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">org.clojars.strad/nginx-crate</span> <span class="s">"0.8.3"</span><span class="p">]])</span>
</span></span></code></pre></div><h2 id="main-configuration">Main configuration</h2>
<p>Now we can start configuring the server. To properly understand this
walkthrough, keep <a href="http://palletops.com/doc/reference-0.8/">Pallet documentation</a> handy.</p>
<p>I put my configuration in the
namespace <code>net.quanttype.ops.core</code>. Here’s the ns form:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">ns </span><span class="nv">net.quanttype.ops.core</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="ss">:use</span>
</span></span><span class="line"><span class="cl"> <span class="nv">pallet.actions</span>
</span></span><span class="line"><span class="cl"> <span class="nv">pallet.api</span>
</span></span><span class="line"><span class="cl"> <span class="nv">pallet.compute</span>
</span></span><span class="line"><span class="cl"> <span class="nv">pallet.crate.nginx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="ss">:require</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nv">pallet.crate</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">defplan</span><span class="p">]]))</span>
</span></span></code></pre></div><p>Pallet does not support DigitalOcean’s API, but once you’ve acquired a server,
you can just give Pallet a list of IPs of your server and it will happily
connect there. To do so, you need to define a <a href="http://palletops.com/doc/how-tos/using-pallet-with-existing-servers/"><code>node-list</code></a> compute
service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">digital-ocean</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">instantiate-provider</span>
</span></span><span class="line"><span class="cl"> <span class="s">"node-list"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:node-list</span>
</span></span><span class="line"><span class="cl"> <span class="c1">;; A list of nodes: [name group IP operating-system]</span>
</span></span><span class="line"><span class="cl"> <span class="p">[[</span><span class="s">"quanttype.net"</span> <span class="s">"web"</span> <span class="s">"37.139.12.210"</span> <span class="ss">:ubuntu</span><span class="p">]]))</span>
</span></span></code></pre></div><p>Pallet also needs to know about the user account it should use. As I already
set up an account that uses my default SSH key and has passwordless sudo, all I
need is to tell Pallet the username.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">my-user</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">make-user</span> <span class="s">"arcatan"</span><span class="p">))</span>
</span></span></code></pre></div><p>First, I’m going to want some packages for easier usage of the server. I
created a <a href="http://palletops.com/doc/reference/0.8/node-types/#server_specification">server specification</a>, which install my packages. In
Pallet parlance phase is a sequnce of actions to be executed. Different phases
are applied at different times - <code>:bootstrap</code> is applied when a new node is
started. I also set my shell to be zsh.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">with-my-packages</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">server-spec</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:phases</span> <span class="p">{</span><span class="ss">:bootstrap</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">plan-fn</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">packages</span> <span class="ss">:aptitude</span> <span class="p">[</span><span class="s">"git"</span> <span class="s">"zsh"</span> <span class="s">"vim"</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">user</span> <span class="p">(</span><span class="ss">:username</span> <span class="nv">my-user</span><span class="p">)</span> <span class="ss">:shell</span> <span class="ss">:zsh</span><span class="p">)}))</span>
</span></span></code></pre></div><p>Next, I will set up <a href="http://wiki.nginx.org">nginx</a>. This is easiest to with
<a href="https://github.com/rstradling/nginx-crate">nginx-crate</a>. I used <a href="https://github.com/rstradling">Ryan Stradling</a>’s fork which
has been updated to Pallet 0.8. The configuration keys match those of the real
nginx configuration files, so it’s best to see <a href="http://wiki.nginx.org/Configuration">nginx documentation</a>
to figure out what you need.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">http-server-config</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="ss">:install-strategy</span> <span class="ss">:packages</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:user</span> <span class="s">"www-data"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:group</span> <span class="s">"www-data"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:sites</span> <span class="p">[{</span><span class="ss">:action</span> <span class="ss">:enable</span>
</span></span><span class="line"><span class="cl"> <span class="c1">;; Name of the configuration file must be something.site,</span>
</span></span><span class="line"><span class="cl"> <span class="c1">;; because nginx's main configuration files includes *.site.</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:name</span> <span class="s">"quanttype.site"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:servers</span> <span class="p">[{</span><span class="ss">:server-name</span> <span class="s">"quanttype.net www.quanttype.net \"\""</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:listen</span> <span class="s">"80"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:index</span> <span class="s">"index.html"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:root</span> <span class="s">"/var/www/quanttype.net"</span><span class="p">}]}]})</span>
</span></span></code></pre></div><p>With this configuration, nginx serves quanttype.net from
<code>/var/www/quanttype.net</code>. Here’s a plan function that makes sure that the
directory exists and has the proper rights for me to rsync files there.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="nf">defplan</span> <span class="nv">quanttype-directories</span>
</span></span><span class="line"><span class="cl"> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">group</span> <span class="s">"www-data"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">user</span> <span class="s">"www-data"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:system</span> <span class="nv">true</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:home</span> <span class="s">"/var/www"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:create-home</span> <span class="nv">false</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:shell</span> <span class="nv">false</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:group</span> <span class="s">"www-data"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">exec-script</span> <span class="p">(</span><span class="s">"usermod"</span> <span class="s">"-a"</span> <span class="s">"-G"</span> <span class="s">"www-data"</span> <span class="p">(</span><span class="ss">:username</span> <span class="nv">my-user</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">directory</span> <span class="s">"/var/www/quanttype.net"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:owner</span> <span class="p">(</span><span class="ss">:username</span> <span class="nv">my-user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:group</span> <span class="s">"www-data"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:mode</span> <span class="s">"0755"</span><span class="p">))</span>
</span></span></code></pre></div><p>We need another server specification to encapsulate the nginx configuration.
The key here is that it extends <code>(nginx http-server-config)</code>, where <code>nginx</code> is
a function provided by nginx-crate.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">quanttype-server</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">server-spec</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:extends</span> <span class="p">[(</span><span class="nf">nginx</span> <span class="nv">http-server-config</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:phases</span> <span class="p">{</span><span class="ss">:configure</span> <span class="p">(</span><span class="nf">plan-fn</span> <span class="p">(</span><span class="nf">quanttype-directories</span><span class="p">))}))</span>
</span></span></code></pre></div><p>Finally, to pull everything together, I define the <code>"web"</code> group. In the
node-list we defined <code>quanttype.net</code>’s group as <code>web</code> and this group
specification maps it to the server specifications we saw above.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">web-group</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nf">group-spec</span>
</span></span><span class="line"><span class="cl"> <span class="s">"web"</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:extends</span> <span class="p">[</span><span class="nv">with-my-config</span> <span class="nv">quanttype-server</span><span class="p">]))</span>
</span></span></code></pre></div><h2 id="applying-the-configuration">Applying the configuration</h2>
<p>The configuration is applied to the servers with <a href="http://palletops.com/doc/reference/0.8/operations/"><code>lift</code> and
<code>converge</code></a>. I have only one server, so <code>lift</code> is what I need.
Here’s a helper function for executing the config.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">execute</span>
</span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="o">&</span> <span class="nv">args</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">apply </span><span class="nv">lift</span>
</span></span><span class="line"><span class="cl"> <span class="nv">web-group</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:user</span> <span class="nv">my-user</span>
</span></span><span class="line"><span class="cl"> <span class="ss">:compute</span> <span class="nv">digital-ocean</span>
</span></span><span class="line"><span class="cl"> <span class="nv">args</span><span class="p">))</span>
</span></span></code></pre></div><p>Now, open a REPL and use <code>(execute)</code>. Because I didn’t use Pallet to provision
the nodes, I need to run <code>:bootstrap</code> myself.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="nv">user></span> <span class="p">(</span><span class="nf">use</span> <span class="ss">'net.quanttype.ops.core</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">nil</span>
</span></span><span class="line"><span class="cl"><span class="nv">user></span> <span class="p">(</span><span class="nf">execute</span> <span class="ss">:bootstrap</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">user></span> <span class="p">(</span><span class="nf">execute</span> <span class="ss">:install</span><span class="p">)</span> <span class="o">#</span> <span class="nv">custom</span> <span class="nv">phase</span> <span class="nv">by</span> <span class="nv">nginx-crate</span> <span class="nv">to</span> <span class="nv">install</span> <span class="nv">nginx</span>
</span></span><span class="line"><span class="cl"><span class="nv">user></span> <span class="p">(</span><span class="nf">execute</span><span class="p">)</span> <span class="o">#</span> <span class="ss">:configure</span> <span class="nv">is</span> <span class="nv">executed</span> <span class="nv">by</span> <span class="nv">default</span>
</span></span><span class="line"><span class="cl"><span class="nv">user></span> <span class="p">(</span><span class="nf">execute</span> <span class="ss">:restart</span><span class="p">)</span> <span class="o">#</span> <span class="nv">custom</span> <span class="nv">phase</span> <span class="nv">by</span> <span class="nv">nginx-crate</span> <span class="nv">to</span> <span class="nv">restart</span> <span class="nv">nginx</span>
</span></span></code></pre></div><p>Ta-da, everything is ready and the only thing left is to rsync the content to
the server.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Now you’ve seen how to automatically install and configure nginx with Pallet.
Hopefully sharing this helps people to get started with Pallet. I found that
it’s pretty easy to use after a while, but figuring out what to do at first is
hard.</p>
<p>Here’s a to-do list for the future:</p>
<ul>
<li>Testing the configuration locally in VirtualBox</li>
<li>Automating the pre-Pallet steps</li>
<li>Support for DigitalOcean in Pallet</li>
</ul>
<p>Another thing I’ve been looking at is <a href="http://www.docker.io/">Docker</a>. It packages
applications in isolated containers that can be easily deployed anywhere (as
long as you’re running modern-enough Linux). If it catches on, it might a very
good idea for future-proofing the setup. I’m not yet quite sure how it fits in
this picture, but I’m keeping an eye on it.</p>
Tomaatti-Sota
https://quanttype.net/p/tomaatti-sota/
Wed, 14 Aug 2013 00:00:00 +0000https://quanttype.net/p/tomaatti-sota/<p>You know those animated GIFs from the 1990s with a rotating
construction sign, or maybe with a guy digging? This page needs
one of those. But what should you do while I'm searching for a
suitable GIF?</p>
<p>Well. Do you remember the classic Finnish
freeware game <em>Tomaatti Sota</em>? It was made by WizeQuiz
and it had an epic story.</p>
<figure class="hero">
<picture>
<img src="https://quanttype.net/images/tomaatti.png" width="600" height="426.52519893899205" loading="lazy" />
</picture>
</figure>
<blockquote>"Olipa kerran (ja miksei toisenkin) suuri
Tomaatti-Jumala, joka oli huomannut, että hänen kaksi
kansaansa, Tuore- ja PakasteTomaatit, elivät sulassa
sovussa. Niinpä tähän oli tultava muutos. Ja perkele,
sitähän tulikin! Joten nyt he taistelevat loputonta
taisteluaan, kunnes piru heidät pieree..."</blockquote>
<p>I guess you could <a
href="http://www.jonneweb.net/pelit/file/555/tomaattisota/">download
the game</a> and give it a go, for the old times' sake. It
works in DOSBox.</p>