Foreword
This book is dedicated to everyone who has inspired me to never stop growing. I encourage you to do the same.
— Marshall Bowers
Computers
The majority of my life has been spent around and working with computers. The impact of this cannot be understated.
Programming
Programming is the closest thing one can have to a superpower. It opens doors to entire worlds that would otherwise only exist in our minds or the pages of books.
Functional Programming
Languages
A language that doesn't affect the way you think about programming, is not worth knowing.
— Alan Perlis
Haskell
Haskell is an advanced purely functional programming language.
Writing Haskell in the large
Don Stewart, co-author of Real World Haskell mentions the following suggestions for writing Haskell in the large:
I talk a bit about this in Engineering Large Projects in Haskell and in the Design and Implementation of XMonad. Engineering in the large is about managing complexity. The primary code structuring mechanisms in Haskell for managing complexity are:
The type system
- Use the type system to enforce abstractions, simplifying interactions.
- Enforce key invariants via types
- (e.g. that certain values cannot escape some scope)
- That certain code does no IO, does not touch the disk
- Enforce safety: checked exceptions (Maybe/Either), avoid mixing concepts (Word, Int, Address)
- Good data structures (like zippers) can make some classes of testing needless, as they rule out e.g. out of bounds errors statically.
The profiler
- Provide objective evidence of your program's heap and time profiles.
- Heap profiling, in particular, is the best way to ensure no unnecessary memory use.
Purity
- Reduce complexity dramatically by removing state. Purely functional code scales, because it is compositional. All you need is the type to determine how to use some code -- it won't mysteriously break when you change some other part of the program.
- Use lots of "model/view/controller" style programming: parse external data as soon as possible into purely functional data structures, operate on those structures, then once all work is done, render/flush/serialize out. Keeps most of your code pure
Testing
- QuickCheck + Haskell Code Coverage, to ensure you are testing the things you can't check with types.
- GHC + RTS is great for seeing if you're spending too much time doing GC.
- QuickCheck can also help you identify clean, orthogonal APIs for your modules. If the properties of your code are difficult to state, they're probably too complex. Keep refactoring until you have a clean set of properties that can test your code, that compose well. Then the code is probably well designed too.
Monads for Structuring
- Monads capture key architectural designs in types (this code accesses hardware, this code is a single-user session, etc.)
- E.g. the X monad in xmonad, captures precisely the design for what state is visible to what components of the system.
Type classes and existential types
- Use type classes to provide abstraction: hide implementations behind polymorphic interfaces.
Concurrency and parallelism
- Sneak
par
into your program to beat the competition with easy, composable parallelism.Refactor
- You can refactor in Haskell a lot. The types ensure your large scale changes will be safe, if you're using types wisely. This will help your codebase scale. Make sure that your refactorings will cause type errors until complete.
Use the FFI wisely
- The FFI makes it easier to play with foreign code, but that foreign code can be dangerous.
- Be very careful in assumptions about the shape of data returned.
Meta programming
- A bit of Template Haskell or generics can remove boilerplate.
Packaging and distribution
- Use Cabal. Don't roll your own build system. (EDIT: Actually you probably want to use Stack now for getting started.).
- Use Haddock for good API docs
- Tools like graphmod can show your module structures.
- Rely on the Haskell Platform versions of libraries and tools, if at all possible. It is a stable base. (EDIT: Again, these days you likely want to use Stack for getting a stable base up and running.)
Warnings
- Use
-Wall
to keep your code clean of smells. You might also look at Agda, Isabelle or Catch for more assurance. For lint-like checking, see the great hlint, which will suggest improvements.With all these tools you can keep a handle on complexity, removing as many interactions between components as possible. Ideally, you have a very large base of pure code, which is really easy to maintain, since it is compositional. That's not always possible, but it is worth aiming for.
In general: decompose the logical units of your system into the smallest referentially transparent components possible, then implement them in modules. Global or local environments for sets of components (or inside components) might be mapped to monads. Use algebraic data types to describe core data structures. Share those definitions widely.
Idris
Idris is a programming language designed to encourage Type-Driven Development.
Idris 2
Development is currently underway on Idris 2, the successor to Idris.
Edwin Brady, the Idris author, gave a talk at Curry On London! 2019 on the type-driven development of Idris which showcases some of the new features in Idris 2.
Developing
On NixOS you can use the following shell.nix
in order to get (most of) the dependencies required to build Idris 2:
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "idris2";
buildInputs = [
stdenv
pkg-config
clang
chez
gmp
];
}
You will also need a version of Idris 1 installed. At time of writing the idris
package for NixOS is currently broken, so you'll need to build Idris 1 from source as well:
git clone git@github.com:idris-lang/Idris-dev.git idris-dev
cd idris-dev
stack install --nix
Idris 1 should now be installed to ~/.local/bin/idris
. Make sure that ~/.local/bin
is on your PATH
before attempting to build Idris 2.
Once all of these dependencies are installed, you should be all set to start hacking on Idris 2!
PureScript
PureScript is a strongly-typed functional programming language that compiles to JavaScript.
Rust
Rust is a language empowering everyone to build reliable and efficient software.
Learning Rust
The Rust Programming Language is the best place to start when learning Rust.
Git
Git is a free and open source distributed version control system.
Aliases
I use a number of aliases (defined in my fish config) to optimize my Git workflow. Since I spend so much time in Git, cutting down on keystrokes really goes a long way towards saving time and maintaining my flow.
Here are the aliases that I am currently using:
Alias | Command |
---|---|
gs | git status |
gc | git commit |
gca | git add -A; and git commit |
gd | git diff |
gl | git log |
gp | git pull |
goops | git add -A; and git reset --hard HEAD |
The nice thing about these aliases is that they still expose the entire Git interface. For example, I typically use gca -m "My commit message"
to stage and commit all of my changes in one go.
I also have some additional commands for visualizing Git history defined as fish functions:
function gll
git log --graph --date=short --pretty=format:'%Cgreen%h %Cblue%cd (%cr) %Cred%an%C(yellow)%d%Creset: %s'
end
function glll
git log --graph --stat --date=short --pretty=format:'%Cgreen%h %Cblue%cd (%cr) %Cred%an%C(yellow)%d%Creset: %s'
end
These aliases may change over time, so check out my dotfiles for any changes.
Updating forks
After forking a repository you'll typically want to keep your fork updated with changes from the upstream repository.
You'll need to setup the upstream repository as a remote:
git remote add upstream git@github.com:<USERNAME>/<REPO>.git
Once you have an upstream remote, you can use the following commands to update your fork:
git fetch upstream
git checkout master
git merge upstream/master
Git GUIs
While I spend the majority of my time in the Git CLI, I do find it handy to keep a graphical Git client around for certain tasks. I tend to prefer a GUI when I'm browsing through history or staging individual parts of a file.
My Git GUI of choice is Sublime Merge. It's a phenomenal piece of software.
Operating Systems
NixOS
NixOS is a Linux distribution with a unique approach to package and configuration management. Built on top of the Nix package manager, it is completely declarative, makes upgrading systems reliable, and has many other advantages.
I have been using NixOS as my Linux distro of choice since November 2019.
Home Manager
Home Manager is a project that allows you to manage your user environment using Nix, much like how you manage your system's global configuration. This is preferable, as it means that multiple users on the machine can have control over their own environments. In my case, I am the only user on my machine, but I still use Home Manager as it means I don't need root privileges to install new packages.
Binaries
Downloading and attempting to run a binary on NixOS will almost never work. This is due to hard-coded paths in the executable.
This is one thing to keep in mind when running NixOS. Just today I tried to run a binary off GitHub and was confused when it didn't work.
Communication
Notes on communication using computers (or other forms of technology).
Matrix
Matrix is an open network for secure, decentralized communication.
Clients
Riot is a client for the Matrix protocol. It has a web client as well as desktop and mobile clients.
IRC Bridge
You can use Matrix to bridge to IRC channels. This allows you to use Matrix in place of an IRC bouncer, which means you can stay connected to IRC even if you close your Matrix client.
Network | Room Alias Format | Appservice User |
---|---|---|
freenode | #freenode_#<channel>:matrix.org | @appservice-irc:matrix.org |
See here for an up-to-date list of bridged IRC networks.