← back to home

Sum types

· 353 words · 2 min read

Zinc's error handling right now is C's error handling: return an int, set the global errno variable, the caller should checks it every time. Which is to say there isn't one. I read the error handling of other languages: The try-catch. It hides the control flow: an error can fly up through three call frames and get caught somewhere I can't see from where the throw happens. Every function that could fail should return a flag (valid, invalid) and the payload (the value for normal returns OR the exception triggered by throw) Then if the throw is inside a try block goes to the catch, else it returns untile it catch a try block. This is not clearly the best approach for a system language where the control flow must be explicit.

The Go style if err != nil is the opposite. It is too verbose and every function returns a tuple with value and error and every caller checks. Zinc has tuples, so I could do this but it's too verbose for me. Every call site grows three lines of boilerplate.

Then there's Rust: a generic Result<T, E> and the ? to propagete. This is the one I actually want. But it stands on generics and Zinc doesn't have them, and I'm not sure I want to implement generics right now.

So sum type is the solution for Zinc. It's a value that's one of several things. T | Error is already that. I don't need generic Result. A function that can fail just returns the success type or the error type written inline:

readConfig :: Config | Error (...) {
    ...
}

It doesn't need generics and it's also shorter to type than the Result. Now wha'ts missing is the ? propagation Today, puling a value out and passing the error up means a match or an if := at every level, which drags me back toward the Go verbosity I was trying to escape. So the sum type made the error cheap to represent. It didn't yet make it cheap to forward. That's the next feature to implement.


← back to home · rss