Excellent question! I started the blog with the ambition of writing a book with the same title. Obviously I haven’t succeeded in that goal…

]]>Suppose you want to define a partial “sign” function, returning the sign of a non-zero floating-point number. To handle the partiality, you use Maybe:

> sign :: Float -> Maybe Int

So sign 3 = Just 1, sign (-3) = Just (-1), and sign 0 = Nothing.

It’s the Maybe structure of the output that is going to be more relevant than any structure of the Float input, so a co-program is called for:

> sign x > | ... = Nothing > | otherwise = Just ...

You can squeeze the program into this structure, if you try hard enough:

> sign x > | x==0 = Nothing > | otherwise = Just (x / abs x)

But you might then worry about rounding errors, about the cost of division, about unnecessary distinction between fractional types like Rational and real types like Float, and so on. You can assuage those worries by using comparison instead of division:

> sign x > | x==0 = Nothing > | otherwise = Just y > where y = if x<0 then -1 else 1

However, I don’t think either of those programs is as good as a three-way one:

> sign x > | x > 0 = Just 1 > | x==0 = Nothing > | x < 0 = Just (-1)

I would still call that a co-program, even though it has three cases when Maybe has two.

Alternatively, you could make sign return a result of a three-valued type (Maybe Bool, or Ordering); then the obvious co-program structure is also the best structure.

]]>That’s neat!

]]>Delayed reply again – sorry. I don’t know about μF × μG → A. If both F and G are strong (so you can distribute A × FB → F(A × B) and similarly for G), then you can unpack μF × μG and distribute to F(μF × μG) × G(μF × μG), then make recursive calls to get FA × GA, so you’d need an “algebra” FA × GA → A to finish off. But that feels more like a cartesian product than zipping. And I don’t think I know how that relates to initiality.

]]>Let S be the preorder of possibly infinite sequences of integers, and x <= y if x is a prefix of y. Then Yoneda embedding for this category tells us x = y holds exactly when for any (possibly infinite sequences) z, z <= x iff z <= y.

Because the full subcategory of finite sequences is _dense_ in S, we can restrict the range of z to only finite sequences. This is the Approx lemma.

Similarly for ApproxWhile, we take the dense subcategory to be the sequences that are bounded by some b_i in the statement of ApproxWhile lemma.

I think this is a nice little example of restricting the Yoneda embedding to a dense subcategory, which is still faithful.

]]>