Go Lift
John Cinnamond (speaker)
This talk is about how Category Theory can help you write better code, but without using the words "Category" or "Theory" (or monad or functor or any of the scary terminology). We'll look at how the idea behind "Errors are Values" from the Go blog can be applied to different kinds of programming problems, and how we can make our code easier to compose by moving units of control flow into types. (That sounds a bit fancy, but it's much more straightforward than it sounds.)
Note: This post was live-blogged at dotGo 2017. Let us know on Twitter (@sourcegraph) if we missed anything. All content is from the talk; any mistakes or misrepresentations are our fault, not the speaker's.
Part 1: Constant Interruptions
- Connect to the server
- Send first command
- Wait for ok
- Send second command
conn := net.Dial("tcp", "server:9876")
What if this goes wrong?
conn, err := net.Dial("tcp", "server"9876")
... John goes through several more examples of function calls where you could easily forget to handle an error, and would have to interrupt your development to handle the error. Instead of understanding what the code does, he sees this instead:
Error handling is a good thing, but it shouldn't get in the way.
if err != nil
Part 2:
conn.Write(command1)
- Not really the network
- An abstraction
- A value
- ...containing data about the connection
- ...and behavior to use it
- Determined by the type
Let's add to the behavior of net.Conn
to handle errors => create a new type
Add error handling:
Repeat for other errors
We still handle the error, but the error handling doesn't get in the way.
But, we introduced a new abstraction. Abstractions have costs. Some details are hidden, but this is nothing new as we use abstractions all the time. Is this abstraction appropriate?
Part 3: Division
Given 3 numbers (a,b,c) => a/b/c
Let's solve this badly.
if b == 0
and if c == 0
resembles err != nil
.
To solve this in a better way:
- Create a new type
- Wrap the initial value
- Wrap the behaviour
- Wrap the conditionals
Create a new type:
Wrap the initial value:
Wrap the behaviour:
Wrap the conditional:
Put it all together
This is the same approach as error handling:
- Create a new type
- Wrap the initial value
- Wrap the behavior
- Wrap the conditional
To understand this let's look at the shape of the code.
Lift the initial value into a new type
Lift the behavior
Lift the conditionals.
The key insight is that we can write our code as a simple composition of steps.
Part 4: Building a webapp
John has been working on a new project http://doesgohavegenericsyet.com
We know how to do this
- Create a new type
- Lift initial data
- Lift behavior
- Lift control flow
Create a new type
Lift initial data
Lift behaviour
Lift control flow
Compose the functions
Maybe don't rewrite your request handlers like this. John is not trying to tell you how to write code. He is trying to give you something new to think about. Think about the shape of the code. Think about using types. John wants to give you new tools to cope with complex code. It's up to you to decide how to use them.
Epilogue
On further thought this talk isn't about introducing a new tool to use, it is more about having a deeper understanding of the theory behind the talked about tool.
We solve problems by breaking them into smaller pieces, but the we need to join the pieces back together again. We need to understand the forces at play. Mathematics gives us this understanding. Mathematics lets us achieve more. Mathematics is pretty useful.