Managing error types in function composition

Using function composition to compose translations and other compiler passes makes it really clear what passes are involved. Such compositions are a great way to write code, especially in the UFT driver, but it’s not always easy to get them to type check. This short note presents some heuristics that make it easier.

The heuristics include a trick: I redefine ! to be a synonym for Error.map. I can then write >>> !, which looks sort of like an infix operator, even though it isn’t really. With that trick, I can compose two functions \(f\) and \(g\) based on what type comes out of each function. (I assume that no error type ever goes directly into either function.)

Here’s a table of types:

Function compositions
\(f\)’s type operator \(g\)’s type (or \(g\)) composition’s type
'a -> 'b >>> 'b -> 'c 'a -> 'c
'a -> 'b error >>> ! 'b -> 'c 'a -> 'c error
'a -> 'b error >=> 'b -> 'c error 'a -> 'c error
'a -> 'b error list >>> Error.list 'a -> 'b list error

And here’s an algebraic law:

f >=> (g >>> h) ===  f >>> ! g >=> h

I recommend using this law from left to right, so it can be used to un-nest pipelines.