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.)
If neither function returns an
error
type, compose them with>>>
.If both \(f\) and \(g\) return
error
types, compose them with>=>
.If \(f\) returns an
error
type but \(g\) does not, compose them with>>> !
.If \(f\) returns ’b error list, for any
'b
, use>>>
to compose \(f\) withError.list
. In this situation, no other \(g\) is useful. But the composition \(f\) >>> Error.list can then be composed with a function \(g'\) that expects an input of type'b list
.
Here’s a table of types:
\(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.