Goodbye 2019, Hello 2020

Wrap-up and new goals
The text "wrap-up 2019, new goals 2020" across a graphic that mixes a variety of logos: this blog, Kubernetes, OpenShift, Haskell, Emacs, and Nix

One year gone, another one about to begin. Bring it on.

It's the end of the year and there's round-ups and predictions everywhere, so I think it's an appropriate time to take a step back and look at what I've achieved this year and what I have in mind for next year.

2019

Let's move in a chronological fashion. In 2019, my list of goals was just a slightly-longer-than-usual Slack message to my friends. They were pretty vague, but still hold some value as a set of overarching goals.

Goals

Containers
Before 2019, I'd hardly ever touched a container. I'd heard lots about them on various podcasts and was really intrigued. I wanted to 'really familiarize myself with the whole thing' (how's that for a vague goal?).

A year later, do I think I've succeeded? Yes. It's stated in a very unclear fashion and can be taken to mean a lot of things, but since then, I've been working pretty closely with containers and Kubernetes pretty much the entire time. Yes, there's still some things that are unclear to me, but overall, I think I understand containers well enough to have cleared that goal.

NixOS
I installed NixOS on my personal computer pretty much exactly a year ago. It was (and still is) my first real foray into the Linux world. I'd used Ubuntu for some university assignments previously, but that's it. It took me a good few weeks to get my external monitors up and working, but these days, most everything is running pretty smoothly (except for that NVidia GPU I've got ...).

I said that I'd 'just got it set up over Christmas, but there is still a lot left to be done and a lot of things to discover.' A year later, I'm fairly comfortable in NixOS, I use Nix for a lot of work related stuff, and I love it. I'd say I've finished the NixOS setup and discovered much, though there is still a lot left to be done. What I have achieved is: I have a stable operating system that works as I expect and with a window manager / desktop environment that I enjoy. I'm happy with the OS state of things for now. Achieved.

Give a talk
Said at the time: 'This is something I've been wanting to do for a while, and I think this year is the right time, once I'm out of uni and have a bit more time (and experience) on my hands.'

This is probably the one goal I set for myself that is the easiest to measure. I spoke internally at my company about Algebraic Data Types, I held a workshop (still counts) on re-implementing some basic data types and creating opaque types at Oslo Elm Meetup, and I held a talk about the new async/.await features in Rust at Rust Oslo. Done.

Non-goals

In addition to the above goals, I also achieved a few other things that I'd like to highlight in this post. These are items that I didn't put on my to-do list, but that I consider significant enough to mention.

Kubernetes/OpenShift
I started working in a team where OpenShift is an important part of the workflow, and interacting with it became a natural part of my day-to-day work.
Started blagging
Took me a while, but I finally got going. It's been a lot of work, but very rewarding.
Became a co-organizer of the Rust Oslo Meetup group
I'm excited to see what we can do to further engage the Rust community in the vicinity and what we'll be doing in the coming year.

2020

Looking forward to 2020, what do I want to achieve? What skills do I want to develop? After some thought, I've come to the conclusion that my 'theme' for 2020's goals is mastery. I've found tools that I really like and that align with my way of thinking. However, I don't feel like I've fully mastered any of them just yet. Most of my goals will revolve around improving my skill with tools that I already know and love.

Goals

Here's a loose definition of my goals for the coming year.

General goals

These are the general goals that I'm setting for myself. These are the ones I will actively be pursuing and working towards.

More infrastructure work
Building on the 'containers' goal of this past year, I want to keep working on infrastructure: Kubernetes, CI/CD pipelines, containers, the whole deal. I went through a Red Hat training course this year, working towards a certification as a 'Specialist in OpenShift Application Development'. I failed the exam, but learned a lot in the process. Let's see if I can use that to pass! Apart from that one specific goal, this post is more about making myself familiar with the tools and getting really comfortable with them. You know, mastering them.
More Nix
It's been a year of NixOS, and it's been great. Now I want to get really comfortable with the difference between build.nix, default.nix, shell.nix, how nix-build works, etc. Specifically, I'd like to be comfortable with setting up a Haskell dev env with Nix and Cabal.
More Emacs
Emacs has been my go-to editor for a year and a half now, and I still get stuck in weird states sometimes. This coming year, I want to get more comfortable with it on a deeper level. Step one: Read the built-in Emacs manual.
More community engagement
I gave some talks and workshops, both internally and at local meetups this past year. I want to do more of that in 2020. Also: apply for more conference talks. I applied to a few this past year, but didn't get any of them.
Programming Language Theory / Compilers
I've had an interest in type systems and PLT for a while. While writing this post, I realized I should do something about it and ordered Types and Programming Languages by Benjamin C. Pierce. I don't know enough about it to set any clear goals just yet, however.

Languages

While I'm not going into this coming year with any clear language goals in mind, I'd like this to serve as a recording of what languages have me the most curious at the moment. I'm not making any clear commitments to learning any of these, but I think it's important to keep an open mind and keep exploring new languages and new concepts. As Scott Wlaschin talks about in his talk /Four Languages from Forty Years Ago/, you should look for languages that expand your mind and help you think about problems in a new way. He awards languages that achieve this a 'Galaxy Brain Seal of Approval'. There is an Alan Perlis quote in the slides saying that 'A language that doesn't affect the way you think about programming, is not worth knowing'.

Haskell
Yeah, I've been doing a bit of this, but not enough. I've got the basics down, but I need to put it into practice. I want to build something; a simple Web API or command line interface should be enough.
Lisp
Doesn't matter whether it's common lisp, scheme, (typed) racket, or clojure: I want to get comfortable with lisp. If nothing else, at least it'll help me configure Emacs.
Elixir
This is the wildcard on the list. I have not really looked much at it at all, but I hear great things. Should offer some nice Galaxy Brain moments.

SMART goals

The goals listed above are generally quite vague, and it'd be hard to say for sure whether I've achieved them or not. To make them more actionable, let's create a set of SMART goals that I can tick. What are SMART goals? Of course, Wikipedia has an article on it, but the short version is that SMART is an acronym that stands for 'Specific, Measurable, Achievable, Relevant, Time-bound'. Organized by topic, this is my list:

Programming Language Theory
I don't have enough insight into this to make any clear goals for it, but I've had a brief look at the Types and Programming Languages book. It's about 650 pages long. I'd say I should get through it by July at latest. In short: Read Types and Programming Languages by July.
Community engagement
  • Give at least three talks/workshops at community events.
  • Apply as a speaker to at least three conferences.
Emacs
By February 1^st, I want to have gotten through the Emacs manual. At this point, I may decide to try and (at least start to) configure Emacs from the ground up (rather than relying on Spacemacs), but that is not a requirement.
Nix
Write about what the various .nix files are and what their intended use is. Should discuss at least build.nix, default.nix, and shell.nix.
OpenShift
Pass the Red Hat Certified Specialist in OpenShift Application Development exam.
Containers / Infrastructure / Haskell / Nix
This is a set of goals that apply to broad range of topics. I want to manage a Kubernetes cluster somewhere and create a Haskell API that serves requests. The Haskell project should use Nix for as much as practically possible. What sort of data the API serves doesn't matter.

Deadlines:

April 1^st
Have Kubernetes cluster up and running.
June 1^st
Have the Haskell API up and running.

And that's what I'm planning to look into in the coming year. As always: plans change, life happens, and things inevitably don't go as expected, but it's nice to have a little something to look back on, at least. What do you think? Do you have any clear goals?


This is the weapon of a functional programmer. Not as clumsy or random as a for-loop; an elegant weapon for a more civilized age.

Taking a step back from a pretty dense chapter on more functional patterns, this time we're looking specifically at one thing: recursion. Chapter 8 of the book covers this in pretty good detail, giving a fairly wide range of examples, but I'll be trying to distill it down to its essence in this post. Let's start at the very beginning, why don't we?

What is recursion?

A recursive function is---in its simplest terms---a function which calls itself. As the book describes it: "recursion is defining a function in terms of itself via self-referential expressions." This means that until some condition is met, the function will keep calling itself. If this condition is never met, the function never terminates. Recursion is an important concept in Haskell, because it gives us a way to express indefinite computations.

Base cases

To reiterate: given that it terminates, a recursive function is a function that will keep calling itself until some condition to stop recursing is met. This condition is often known as the base case. For instance, let's look at writing a factorial function, which is a function that multiplies all positive integers (${1..n}$) up to and including the number we're calculating for. In maths, it's commonly written using an exclamation point, such as $4!$.

So we're all on the same page about what the function should do, $4!$ is evaluated as $4 \cdot 3 \cdot 2 \cdot 1$, which is equal to $24$.

To make this a recursive function, we know that for any number $n$ greater than one, it is the result of the factorial of $n-1$ multiplied by $n$. In other words, for $n$ greater than one, $n! = n \cdot (n-1)!$. If $n$ is $1$, then $n! = 1$. In our case, the function is not defined for integers less than one.

Based on the little analysis above, we now know that the value of $n!$ is recursively dependent on the value of $(n-1)!$ and so on. We have also identified our base case: $1$. So how would we go about writing this out? How about something like this:

    factorial :: Integral a => a -> a
    factorial 1 = 1
    factorial n = n * factorial (n - 1)

Note that this function does not terminate if applied to a value less than $1$. This is an issue that we'll come back to shortly.

Recursion as indefinite function composition

One thing that the book mentions that I found quite interesting, is that you can think of recursion as indefinite function composition. When composing functions, all we do is pipe the output of one function into another function. Recursion is the same thing, except it's always the same function. We can use this to create functions that will get applied an arbitrary number of times. Indeed, taking it a step further, we can write a function that takes a function, an argument, and the number of times to apply it, and then applies the function the set number of times:

     applyNTimes :: (Integral a) => a -> (b -> b) -> b -> b
     applyNTimes 0 _ b = b
     applyNTimes n f b = f . applyNTimes (n - 1) f $ b

Using this, we can create an increment function:

     increment :: (Integral a, Num b) => a -> b -> b
     increment times = applyNTimes times (+1)
     -- increment 5 2 = 7

A contrived example, but it's a pretty cool effect!

Bottom

I mentioned when talking about the factorial function that it doesn't terminate for every possible value we can apply it to. A function that never terminates is one of the ways we can play with bottoms in Haskell (and no, it's not a kinky as it sounds!).

In Haskell, bottom (symbolically: ) is used to refer to computations that do not result in a value. The two main varieties of bottom are computations that fail with an error (partial functions) and computations that fail to terminate (such as with infinite recursion and the factorial example above). When you can, stay away from bottom.

Definitons

This chapter only comes with a single definition: recursion. The funny thing would be to tell you to start from the top, but let's be boring and do it the proper way:

Recursion
Recursion is a means of computing results that may require an indefinite amount of work to obtain through the use of repeated function application. A recursive function will usually have at least one case that calls itself and a base case that stops the recursion.

Let's read: Haskell Programming from First Principles, pt VIIc

More Functional Patterns: Function composition and pointfree style

Hey, and welcome back to the third and final part of chapter 7 of Haskell Programming From First Principles! Today we'll be looking at function composition and pointfree style; two related topics that are very commonly seen in Haskell.

Function composition

Let's start of with function composition. Function composition allows us to create new functions by combining existing ones. The one criterion for composing two functions is that the return value (range) of the first function matches the input value (domain) of the second function. In Haskell, we use the dot operator (.) to compose two functions.

Let's inspect the type signature of the . operator and see if we can unpack it:

    (.) :: (b -> c) -> (a -> b) -> a -> c

At first, this may seem a bit complicated, so let's break it down. The . operator goes between two functions (like this: f . g) and returns a function that goes from a to c. What it does is to simply 'glue' the two provided functions together.

I quite like Scott Wlaschin's explanation of it from his talk The Power of Composition (this link takes you to the point in the talk that talks about gluing together functions, but I highly recommend checking out the whole thing if you're interested), where he talks about gluing together a transformation from an apple to a banana, and a function from a banana to a cherry to create a function from an apple to a cherry.

The simplest way to explain composition may be to say that you perform an operation on a value, and then pass the result of that operation to the next function. That new function which puts those two together is the composed function. You might see an example like this:

    (f . g) x = f (g x)

This tells us that applying f composed with g to x is the same as applying g to the argument x and then applying f to the result of that. Note that the . operator might initially appear to read backwards: It's the last function to get run that gets written out first. This is because of it's mathematical roots, where people use the '∘' symbol for composing functions (so our f . g would be $f ∘ g$). I suggest reading the symbol as 'after', which makes it 'f after g', for instance.

If this is all a bit abstract and jargon-y, why don't we look at a concrete example. Say we want a function that tells us whether a string is of even length or not. In that case, we can express the full function as a composition of two smaller functions (the signatures have been simplified/specialized for the sake of example; check the REPL for more): even :: Int -> Bool, and length :: String -> Int (so it'd be 'even after length'). Notice that the return type of length matches the input type of even, and that the type of isOfEvenLength matches the combination of the functions.

    isOfEvenLength :: String -> Bool
    isOfEvenLength = even . length

The astute reader (that's you!) might notice that the function we defined as the composition of the other two functions (isOfEvenLength) doesn't list any arguments in its definition. This might look strange but it takes us nicely into our next point: pointfree style.

Pointfree style

Pointfree style is a way of writing functions without specifying their arguments, most notably when working with composition. It's called 'pointfree' because the arguments are also known as 'points'. There are arguments both for and against writing pointfree style, and it's true that excessive use may cause code to become harder to understand, but used sensibly it can make code tidier, cleaner, and let's us put the focus on the transformations, the functions, rather than the data supplied to them.

Using one of the examples from before, we now get:

    (f . g) x = f (g x)
    -- the above now becomes
    f . g = \x -> f (g x)

    -- similarly, we can add more functions
    f . g . h = \x -> f (g (h x))

How does this work? Well, it all goes back to the lambda calculus and currying. Let's take a step back and look at how you could write an alias for a function in Haskell:

    f a b = -- ... implementation

    g = f

In the above code, we have defined g as simply being the same as f. We'll still need to provide it with the same arguments (a and b), but it's just given a different name. If we want to, we can choose to define it as a specialized version of f, with the first argument already applied:

    f a b = -- ... implementation

    g = f 2

In this case, g is a partially applied version of f and is a function from one argument to a result (whatever f returns). We could also write the above as g x = f 2 x, but because of how partial application works, there's no need to do this. We've already said that g is the same as f applied to 2, and that naturally returns a function from one argument to the result.

Similarly, we might do it for something like map. Say we want a function that doubles all values in a list:

    doubleVals = map (*2)
    -- is the same as
    doubleVals xs = map (*2) xs

The two definitions above are equivalent, they're just written out differently.

Whether you prefer pointfree or 'pointful' styles is an individual thing, and is probably based on experience and circumstance. As mentioned earlier, too much and it makes your code hard to read, too little and you're writing out way more than what you need to and taking focus away from the important parts. Like with so many things, try and apply the Goldilocks principle. As always, the Haskell wiki has more detailed information on the topic, so go give that a read if you fancy.

Definitions

As it's the last post for this chapter, let's go over the chapter definitions.

Anonymous function
Often known as a lambda. It is a function that isn't bound to an identifier and is instead just used as an argument to another function or the like. For instance, \x -> x is an anonymous version of the id function (id x = x).
Currying
The act of transforming a function which takes multiple arguments to a series of nested functions that each take a single argument and return the next function in line (or the result if we're at the end). In Haskell, every function is curried, and this is baked into the language so you don't have to worry about it.
Pattern matching
A way to deconstruct various data types to do something with---or based on---their contents.
Bottom
Bottom is a non-value used to denote that a program cannot return a value or a result. A simple example would be an infinitely looping function, but values that don't handle all their inputs and throw errors also apply here. We'll talk more about Bottom in the chapter on non-strictness.
Higher-order functions
Functions that take other functions as arguments or return functions themselves.
Composition
A way of gluing together multiple functions such that each each function is applied to the result of the next function. Creates a pipeline of transformations.
Pointfree
Also known as 'tacit programming'. Programming without mentioning arguments explicitly.