I've been trying to write a blog post explaining the difference between functional and imperative programming and I've failed multiple times due to writer's block
Instead, I'm going to dump my thoughts here on a Twitter thread to finally get this out of my head 🧵 (1/16)
Okay, so the difference is:
Imperative programming is a style that predominantly uses linear programs and linear data structures
Functional programming is a style that predominantly uses tree-like programs and tree-like data structures
I'll explain …
Linear data structure = lists
Linear programs = statements
Tree-like data structure = algebraic data type
Tree-like program = expression
A linear data structure is basically a sequence of 0 or more elements. For example:
[ 2, 3, 5 ]
Imperative programs tend to overemphasize arrays/lists/vectors compared to their functional counterparts
A linear program is a sequence of 0 or more statements. For example:
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn name
Imperative programs tend to overemphasize organizing code around statements
A tree-like data structure is an algebraic data type that permits arbitrary branching and nesting
A more mainstream example is JSON
A less mainstream example is an abstract syntax tree (literally a tree-like data structure)
Functional programs tend to prefer tree-like data
A tree-like program is an expression, which permits arbitrary branching and nesting
For example, this expression is branched and nested, with each operator corresponding to a node in the syntax tree:
(x < y - 1) || (y + 1 < x)
Functional programs tend to prefer expressions
It's not an accident that control flow parallels data structures
If you overemphasize linear data structures then you need to overemphasize linear control flow (e.g. for loops or while loops) in order to process them
Similarly, tree-like data works better with tree-like code
For example, this is why functional languages and programming styles tend to be overrepresented in compilers
It's easier to manipulate abstract syntax trees if your language is ergonomically optimized for tree-like computation (expression-oriented programming)
This post of mine goes into more detail about the correspondence between data structures and control flow a bit more in the context of functional programming: haskellforall.com/2021/02/folds-…
A classic example of the imperative/functional dichotomy is Docker vs Nix
A Docker file is predominantly a linear sequence of build instructions
A Nix derivation is predominantly a tree representing the dependency graph
Docker = more imperative
Nix = more functional
I say "predominantly" because Dockerfiles can now have multiple FROM statements (which makes them more functional) and Nix builds can locally run shell commands (which makes them more imperative), but you get the idea
Functional or imperative programming is not necessarily inherent to a programming language or tool. You can write functional code or imperative code in, say, JavaScript
That said, one style tends to dominate in most programming language communities
One example is Go vs. Haskell
Go is mildly hostile towards algebraic data types and expression-oriented programming, and Go is widely regarded as imperative
Haskell is mildly hostile to statements and arrays and Haskell is widely regarded as functional
You can program in a functional style in Go, but you'd be going against the grain (both the language and the community)
Vice versa, you can program in an imperative style in Haskell, but you'd also be going against the grain
I'm not the first person to explain functional programming in this way. This way of explaining things dates back at least to Landin's "The Next 700 Programming Languages": cs.cmu.edu/~crary/819-f09… (16/16)
• • •
Missing some Tweet in this thread? You can try to
force a refresh
I have a new type-checking brain worm that I need to get out of my head:
What if you built a type inference algorithm without any unification variables?
I know that sounds silly, but hear me out on this … 🧵 (1/24)
The motivation behind this is my search for the "holy grail" of type-checking algorithms that's both simple/compact to implement but also gives okay type inference
The inference needs to be better than, say, Dhall but it doesn't have to be great: some type annotations are okay
Bidirectional typing was originally the closest thing I found to this requirement: it requires a few more annotations than some algorithms but it's relatively simpler to implement and extend and handles subtyping well. For more details, see: haskellforall.com/2022/06/the-ap…
My biggest complaint about free speech discourse is that it's way too simplistic. Too many people present issues as:
• binary choices instead of a multi-dimensional spectrum
• absolute rules instead of contextual choices
🧵
For example: am I for or against "cancel culture"?
I can't really give a good answer that, because I don't think it's that simple
My actual decision for whether or not to cancel someone is complicated: it depends a lot on the person's reputation, power, and behavior or their willingness to improve, compromise, or act in good faith
It also depends on how their point of view materially impacts others' lives
One thing I'm sensitive to as a manager is ensuring that responsibility and autonomy are correlated. In particular, I try to avoid situations where someone has been given responsibility but not autonomy or, vice versa, been given autonomy but not responsibility
One example is a "senior" engineer who has broad latitude to work on what they want but delegates thankless tasks to other people on the team
They have the autonomy without the responsibility
On the other end of the spectrum you can have an engineer who has been asked to complete a difficult project where someone else controls the design
The implementer has the responsibility but not the autonomy
It's crazy how many server security best practices are cheap or free by virtue of using NixOS
First off, all of your services and configuration files are pinned to absolute paths in the /nix/store that are mounted on a read-only partition
There is a uniform language for configuring the system options, so many best practices are just one NixOS option away. For example, if I want to enable fail2ban for machine I just set:
services.fail2ban.enable = true;
… and I'm done
Did I mention that the set of all available options is easily searchable? You can find them all here: