QWERTY to Dvorak

What I learned

Ever thought about changing up your keyboard layout? Maybe something as small as going from an ISO layout to an ANSI one (hint: tall and thin to short and fat return key), or from Norwegian (or whichever your language of choice is) to American QWERTY? Maybe something more comprehensive, like learning a whole new layout, such as Dvorak, Colemak, or Workman? Well, I've done all of the above, and while the smaller changes can offer some pretty significant benefits, they're pretty easy to get through. The layout-changes are more interesting. I'd like to talk briefly about how I went from QWERTY to Dvorak and what I learned along the way.


First off: you might wonder why I'd do that. I'd love to tell you that I had some grand epiphany and suddenly realized that the QWERTY life wasn't worth living or that I discovered that Mr Dvorak himself was actually my great-great-grandfather; but nah, it was actually just an offhand comment someone made.

See, I had just recently started learning Vim and was talking about it with some fellow students when one of them jokingly suggested I do it in Dvorak instead. As if it wasn't challenging enough already, right? I laughed it off at first, but it sparked something in me. Thirty minutes and some googling later and I had made my mind up. I didn't know whether I was gonna make the switch, but I knew I had to check it out to see what all the fuss was about.

Before starting my degree in computer science (about two years prior to that fateful comment), I had never been much into computers and never properly learned to type, so my keyboard skills weren't exactly award-winning: I wasn't very fast (though not remarkably slow either), and I quite often had to look at the keyboard to find the key I was looking for.

Taking the above into consideration, I realized this was a chance to learn proper form and improve my typing skills; so after some investigation, I decided I was gonna go for it. And seeing this as a chance to pick a layout designed specifically for my purposes, I decided to go for the Programmer Dvorak layout.

Pros and cons

Now, about two years and thousands of key strokes later, I can take stock of what I have gained from switching and what issues arose because of it.

Benefits

Proper touch typing form: This might not seem like a big one, and from what I understand, it's not necessarily the holy grail that some people make it out to be, but it has been a big boon to me. I can't comment on it being better or more efficient than any other technique, but it sure beats my original, awkward, four-fingered approach.

Speed: My typing speed has gone up dramatically. I don't know how many words per minute I could get out of my old QWERTY skills, but it wasn't breaking any necks. I reckon that after about 3--4 months of Dvorak I had already caught up.

Accuracy: A nice feature of learning Dvorak on keyboards where you can't change the physical layout is that you need to memorize the layout because---with the exception of 'A' and 'M'---no letters are placed in the same spot. A side effect of this is that you can't look down on the keyboard to find a key. In fact, that'll probably just confuse you further.

Comfort: There's something about the alternating hand-motion of the Dvorak layout that I really like. In fact, while learning it, I'd often come across sequences of letters that just felt good to write.

It might be more ergonomic: You often hear that the Dvorak layout is more ergonomic and better for avoiding RSI and the like. I can't comment on the veracity of these claims, but it sounds plausible.

Drawbacks

The ramp-up time: It took a while until I got up to a respectable speed. This period where things just don't move as fast as you want can be annoying and mentally taxing.

Extra set-up time: Dvorak is by no means the default layout for any system (as far as I'm aware), and so---if you're using a software-defined layout---you'll always need to go through some extra steps to set it up properly. Even more so if you go for some esoteric layout like Programmer Dvorak (though Linux comes with this available out of the box). This can of course be offset by going for a programmable keyboard (or getting a Dvorak keyboard).

Pair programming: If you're getting help from a coworker, keep a QWERTY layout handy. It's okay to have a little laugh when they stand there, though, perplexed at why nothing seems to work.

Losing touch with QWERTY: When I switched, I switched completely. As a result of this, every time I use a QWERTY keyboard now, I need to go looking for keys and I'm super slow. Though, for some reason, this does not apply to phone keyboards, where Dvorak feels awkward but QWERTY is perfectly fine.

Is it worth it?

Absolutely.

Seeing as I spend most of my day using a keyboard, it makes sense to invest in building skills. This might be 'survivor bias' talking, but I got so much out of it that I'd recommend it to anyone who's interested. At least have a little sniff. If you've got the time and energy, it can be very fun and offer a real feeling of accomplishment. Plus, it'll keep your brain active!

If that was all you wanted to know, you can stop reading now, but if you're looking for some tips on how to learn a new layout, let's finish it off with what I found worked for me.

How to learn

After picking my target layout (Programmer Dvorak), I had to find out how to learn. While it might be tempting to just sit down with a key map next to you (or shuffle the physical keys around if you can do that) and go for it, this is quite likely to be counterproductive for a number of different reasons; the biggest ones being that you're more likely to slip out of proper technique and that it's less efficient (based on my empirical studies with a sample size of one) when it comes to memorizing layout.

So what do I recommend? Glad you asked! I've got some tips for ya:

Use typing training software
These will typically introduce you to one new letter for each hand at a time and slowly work their way through the full layout, often focusing on common patterns. If you want tips on which one to go for, I'd recommend Typing Club; it's free, effective, and even includes some pretty interesting bits of writing to copy as you progress, so you can learn while you learn. Yo, dawg.
Take it everywhere (work, studies, whatever)
Once you're comfortable enough with the position of the keys to not feel like you need to look up where a key is before pressing it (though thinking is okay), start using it all the time. Once you've got the basics down, the most important thing is to get exposure and experience with it. Depending on your situation, this can be more or less acceptable, so keep important deadlines and the like in mind, but don't let that stop you. Keep your other layout around so you can swap back in case of emergency (or pair programming!).
Don't give up!
Even when it seems hard and you're tired of making the same typo for the two hundred and twenty-seventh time this hour, stick with it. Take a deep breath, have a cup of tea, and clear your mind. Go back to the keyboard and start again. Consistency is key. This goes doubly for memorizing fingerings and modified keys: Make sure you always hit the key with the correct finger. Some keys might be awkward at first, but you'll get used to it eventually.
Accurate is the new fast
It doesn't matter how fast you are if you hit the wrong key half the time. It's important to focus on accuracy, especially at the start. Speed will come.

If you decide to embark on this exciting journey, be aware: it won't be easy and the learning curve is pretty steep; but it's not as difficult as you might think. Time and tenacity is all you need: Once you start, you simply need to stick with it.

Good luck!


That's some banging purple, right there. Take note, people.

So you wanna learn Haskell, huh? Well, you've come to the right place. Maybe.

This is the first in a series of posts where I try to firm up my understanding of the Haskell programming language by going through the book Haskell Programming from First Principles by Christopher Allen and Julie Moronuki (ISBN: 9781945388033). I'll work my way through the book and write a summary of the most important bits from each chapter as I go along.

A little bit of background

For a while now, I've been very interested in Haskell and using it whenever I can for my side projects, but I've also been very busy, and haven't had as much time to play with it as I'd wish. I read Learn You a Haskell for Great Good and far too many monad tutorials when I first started out, but since then, it's mostly been a just-in-time sort of process when looking for answers to problems I've come across. So, while I feel I have a basic grasp of the language, some of the more advanced features still escape me.

This book has been on my reading list for a while, but I've not made a serious effort to get through it before. Now, though, I have the time and I have the motivation. It's time to learn Haskell. For real.

Alright, here we go. Deep breaths.

Deep breaths.

Chapter 1: All you need is Lambda

.... aaaand there's not a single line of Haskell in this whole chapter.

I'm sorry to disappoint you, dear reader, but this chapter is entirely about the theoretical underpinnings of the Haskell language: the lambda calculus. But that doesn't mean that we can't make anything of it! Quite the opposite, in fact.

So put on your math hat and do some light stretches.

...

Ready?

Lambda calculus

While a thorough introduction to the lambda calculus is outside the scope of this post (not to mention, not something I'm qualified to do), there are certain concepts that would aid our learning, so let's try a basic introduction.

In the summary of the first chapter, the lambda calculus is defined as "a formal system for expressing programs in terms of abstraction and application." Wow. Such clarity.

Let's see if we can find something more digestible.

Earlier on, the book defines a calculus as a method of calculation or reasoning. In other words, it's a system we can use to solve problems. The lambda calculus is but one process for formalizing one of these methods or systems. In other words, it's a way of thinking about and calculating certain problems and it gives you a set of tools that you can use to solve these.

So why are we looking at the lambda calculus before getting into the code? Well, because functional programming is made up of expressions (values, variables, functions) that model typical mathematical behavior, and understanding how they interact will help us understand how the language works later. Or something.

That's about as much as you'll need to get started. We'll cover anything else as and when it comes up.

Lambda terms

Let's talk about lambda terms, the three basic components of the lambda calculus. They are:

Variables
A variable is just something you can assign a value to. It has no meaning or value, but only exists as potential input to a function. Simple.
Abstractions
An abstraction is a function. It is a lambda term that has a head (a lambda (๐œ†) followed by a variable name) and a body (another expression) and is applied to an argument. An argument is an input value.
Expressions
An expression can be a variable name, an abstraction, or any combination of those two.

A function in the lambda calculus is nothing special. It's just an expression that can be applied to another expression and that returns yet another expression. If this sounds confusing, keep in mind that expressions can be both values and other functions.

A lambda abstraction (that is, a function) looks like this:

$๐œ†x.x$

where everything from the $๐œ†$ to the \(.\) (\(๐œ†x.\)) is known as the head, and the body is what comes after the \(.\) ($x$ in this case). The head binds variables to be used in the body (i.e. function parameters).

Now, an interesting thing to note is that each lambda can only take one argument. Does that mean that in lambda calculus you can only have functions that take a single argument?

Not quite. See, functions that take multiple arguments are actually just nested heads:

$๐œ†x.๐œ†y.xy$

But this is quite verbose, so we'll simplify it by simply writing

$๐œ†xy.xy$

It might seem trivial but it is a very important property of lambdas, known as currying (after Haskell Curry (no, really)).

This is also what allows for partial application. Because an expression can be partially evaluated, we can also apply it to one value and get a new function back.

For a concrete example, let's say we have a function $mul$ that takes two arguments and returns their product:

$mul=๐œ†xy.x*y$

If we want to create a function that doubles a value, we can do that by applying $mul$ to $2$:

$double=mul(2)$

or, written out as the result of the above expression:

$double=๐œ†y.2*y$

The $double$ function expects one argument and will return the double of whatever it receives and is defined as a partially applied $mul$.

Glossary

How lambda terms interact is probably the most important part of this chapter, but let's have a look at some other terms mentioned in the chapter---mostly because you can use them to sounds smart on internet message boards, but also because they help us understand what we're talking about when we're talking about the lambda calculus.

Alpha equivalence
Two expressions are alpha equivalent if they are the same function, even if they are written with different symbols. The following functions are alpha equivalent:
  • $๐œ†a.a$
  • $๐œ†b.b$
  • $๐œ†c.c$
Application
How we evaluate/reduce lambdas.
Beta normal form
When an expression cannot be beta reduced any further, either because there are no more application to do (no more lambdas), or because there are no free variables to apply the function to.
Beta reduction
Evaluating expressions. This is what happens when you go from the expression $๐œ†a.a (2)$ and apply the lambda to the number, thereby ending up with $2$
Bound variables
Variables that are declared in the head of a function (y'know, the parameters)
Codomain
The set of possible outputs of a function.
Combinators
A lambda term with no free variables. In other words, all variables are in the head.
Divergence
Any function that never terminates is divergent (hint: recursive functions that never exit, loops of the while(true) type).
Domain
The set of possible inputs to a function.
Free variables
Variables that exist in a function body that are not defined in the head, such as the $y$ in this expression:

$๐œ†x.xy$

Lambda
The Greek letter ๐œ†. Used to indicate abstractions (functions) and bind variables.
Lambda abstraction
An anonymous function or lambda term. You can think of the head as an abstraction for the body, i.e. something we can put in place of the computation.
Referential transparency (/aka/ purity)
This means that given the same input, a function will always produce the same output.

Conclusion

While this may seem a strange place to start, I can see the logic in doing it, and getting a better understanding of the lambda calculus is helpful anyway, so I'm fine with starting it this way.

Anyway, there should be more code in the later chapters. Until next time!


A terminal emulator icon, followed by the text "awk?"

...ward

Unless you spend a whole lot of time working primarily on the command line, awk is one of those commands you've likely come across a few times, but never really learned how to use properly. Similar to my recent adventures with xargs, though, I recently came across a little use case where I could benefit from using it, so I decided to sit down and look into it.

Even more so than with xargs, this is a really powerful tool, but let's focus on the basics and see what we can make of it.

What is awk?

awk is a command that processes text using programs written in the AWK language.

To understand what it can do, understanding the language it uses is probably a good place to start. Let's use the words of Alfred Aho---one of the creators of the AWK language---from a 2008 /Computerworld/ interview to get a rough grasp of what it is:

AWK is a language for processing files of text. A file is treated as a sequence of records, and by default each line is a record. Each line is broken up into a sequence of fields, so we can think of the first word in a line as the first field, the second word as the second field, and so on. An AWK program is of a sequence of pattern-action statements. AWK reads the input a line at a time. A line is scanned for each pattern in the program, and for each pattern that matches, the associated action is executed.

That may be a bit dense, so let's take it apart:

"AWK is a language for processing [..] text"
AWK is a domain-specific language (DSL) focused on text processing. The awk command expects that its first argument is a script or a string in this language.
"AWK reads the input a line at a time"
AWK is line-oriented and works through the input line by line. (It's actually record-oriented, but the default separator is a newline character, so this is the same thing by default.)
"Each line is broken into a sequence of fields"
Each word in a line maps to a field. These fields are accessed with the $ operator, e.g. $1 for the first word, $2 for the second, and so on. $0 is the whole line. By default, fields are delimited by whitespace (which is why I've called them words) but this can be customized.
"An AWK program is of a sequence of pattern-action statements"
This means it's a sequence of predicates with actions. If a predicate evaluates to true, perform the specified action. If no predicate is specified, it will always evaluate to true, and if no action is specified, it will default to printing the whole line.

Huh? Yeah, this is still a bit confusing, but maybe some examples will make it clearer:

Patterns
This is a predicate to check each line against. It usually takes the form of a regex enclosed in forward slashes. /foo/, /b[ar]?/, /^baz/, /(fizz|buzz)$/ are all examples. Most of the regex skills you have will be applicable here (character sets, character classes, alternations, etc.).

You can also match specific fields against a regex. Only want to match lines where the second field contains 'cheese'? $2 ~ /cheese/

The pattern can also consist of functions and comparisons; so if you wish to act only on lines that aren't empty: length > 0

If no pattern is given, every line will match.

Actions
These are commands telling awk what to execute. They are enclosed in curly braces ({}). This is where you might instruct awk to print a certain field of the string---~print $3~, for instance---or increment a counter if you're counting words: word_count += NF (yeah, I'll get to what NF means in a bit).

If no action is given, awk will print the matching line.

Basic auth awk

That's a quick overview of how the language is structured. Before we start playing with it, let's explore some of the features.

Built-in variables

awk has a number built-in variables, and while I won't cover all of them, these are the ones that I've found the most useful:

~NR~
Gives you the line number of the current match. Could be used for adding line numbers to an input file: ~awk '{print NR, $0}'~. Or maybe looking for lines that contain 'ice cream' is more your speed: ~awk 'ice cream {print NR}'~.
~NF~
This is the number of fields in a line. Useful if you're looking for the last field, either for finding out how many fields are in a line or for seeing if it contains a pattern: ~awk '$NF ~ out {print NR}~
~FS~
The field separator value. This is what awk will use to split each line into fields. By default this is whitespace. If you have a file full of comma-separated values and want to split each line on commas instead of whitespace: BEGIN {FS=","}
~RS~
This is the line ('record') equivalent of the field separator. The default is \n. Say you want to print your PATH over multiple lines: ~echo $PATH | awk 'BEGIN {RS=":"} {print}'~

BEGIN and END

awk lets you supply commands to be executed at the start and end of a script by using BEGIN and END. While they may seem like it, they're not really special at all. Instead, think of them as being patterns that evaluate to true before anything is evaluated and after everything is evaluated, respectively.

BEGIN could be used to set the field separator or initialize a variable.

END is useful for printing out results accrued through the life of the program, such as a word count. If we bring back our word counting example: ~awk '{word_count += NF} END {print word_count}'~

Functions and conditionals

Like most languages, AWK has a number of built-in functions (such as the print and length functions we saw earlier) and also lets you define your own functions if you so please. This is probably overkill for most trivial operations but could come in handy in certain cases.

And AWK has conditionals too! While you can use the if else construct within actions, I'd like to highlight that you can do conditional statements based on the supplied pattern. I.e. ~awk 'foo {print "FOO"} {print "bar"}'~ will print 'FOO' for lines that match /foo/ and 'bar' for lines that don't.

Applications

Now that we've got some idea how it works, what can we use it for? Let's look at some sample applications for it:

Sorting lines in a file by length

Here's a fun little application: Let's take a file, count the number of characters in each line, and then sort it based on the number of characters:

     awk '{print length, $0}'  | sort -n

And if you want to exclude empty lines, try this:

     awk 'length > 0 {print length, $0}'  | sort -n

Friendly PATH

This is the same one that was listed above, and is useful if you're looking for certain entries that can easily get lost when the path is on one line:

        # bash, zsh
        echo $PATH | awk 'BEGIN {RS=":"} {print}'

        # fish
        echo $PATH | awk 'BEGIN {RS=" "} {print}'

Counting words

Another example that was used previously. Count the words in a file or any input stream:

      awk '{word_count += NF} END {print word_count}'

Parsing git logs

Maybe your team has agreed that all commit messages should start with #<task_no> if it relates to a task and #-- if it doesn't. To find all commits that relate to a specific task---say #42---we could do this:

    git log --oneline | awk '/#42/ {print $1}'

Or how about finding the ratio of commits that belong to a task versus those that don't?

    git log --oneline --no-decorate | \
    awk '$2 ~ /#[0-9]+/ {task += 1} {notask +=1} \
      END {printf "Tasks: %d\nNo-tasks: %d\nTask to no-task ratio: %f", \
      task, notask, task / notask }'

(Yeah, the printf function works pretty much the same as in C---so much so that I haven't looked it up yet!)

Killing processes

This is actually what triggered me to look into awk and what eventually led to this post being brought into existence.

There are a number of ways to kill applications from the command line, and for a while, my default fallback has been the classic ps aux | grep <app> combo.

While this lists out all the relevant processes and their process IDs (PIDs), you then have to manually copy the PID and put it into the kill command to shut it down. This is annoying at best, and gets even worse if the process has spawned children that we want to take down as well.

How do we deal with this? Well:

      ps aux | awk '// {print $2}' | xargs kill
  1. ps lists all processes on the system
  1. awk then goes over every line that contains the application name, extracts the second whitespace-delimited word---which in this case is the PID---and prints that.
  1. For the last stretch, we use xargs to feed the PIDs into the kill command, thus killing all the processes.

This works just fine, though it comes with the caveat that it'll also try and kill the awk process (because the application name is part of the command, so it gets listed by ps), but that's only a minor annoyance, and I'll leave fixing that as an exercise for the reader.

Now, I'm sure you can make killall do something quite similar to this, but I've found this way to be more effective (by which I mean: closer to what I expect).

Closing up

Phew. We've learned a lot today, and this post grew much longer than what I had originally imagined---about four or five times longer---but it's been quite the journey and I'm glad you were there with me, partner. I hope you've gleaned some new insight too and that you'll find some application for this in your day-to-day.

See you next time!