Semirings and Predicates

In the article “The Algebra of Predicates and Sorting Functions” we showed how predicates can be made into a monoid. In short, we considered Bool to be a monoid with its operation given by && and identity true, and then defined Predicate<A> to be the type of functions (A) -> Bool. This allowed us to combine predicates in an expressive way, e.g. isEven <> isLessThan(10) is the predicate that literally expresses “is even and is less than 10”.

One thing missing from that discussion is that Bool has another monoidal structure, || with false. We will discuss how to rectify that in detail by defining something called a semiring.

Download a playground of the code in this article to follow along at home!

A tale of two monoids

First recall that a monoid is a type conforming to a protocol that specifies an associative binary operation and an identity element:

The Bool type naturally has two monoidal structures: one defined by (&&, true) and the other by (||, false). Neither of those is the right one, or more important than the other, yet Swift’s protocols force us to choose one for Bool (as do most programming languages out there!). One way around this is to wrap Bool in another type so that we can conform it to the protocol without interfering with Bool:

We now have two different versions of booleans: one that is a monoid under conjunction && and the other under disjunction ||. However, many times you don’t want to separate the ideas of && and || from Bool, but instead have one underlying structure that unifies them. This structure is called a semiring.

Semirings

A semiring is much like a monoid, but it has an additional operation on it, and the operations must play “nicely” together. Formally, a semiring is a type with two operations, denoted by + and *, and two distinguished elements zero and one as identities, satisfying a bunch of axioms:

This last expression describes getting only the integers from 0 to 100 that are even and less than 10, or are the magic number. It reads quite nicely, but it would be even better if we could use || and &&, so let’s define them!

Conclusion

We have now seen how sometimes when a type seems to have two different monoidal structures on it, secretly it may be a semiring where the two structures play nicely together! There can still be times where a type has two different monoidal structures on it such that they do not form a semiring, in which case you must resort to wrapping the type in a new type that provides the custom conformance.

2.) A semiring (S, +, *, zero, one) naturally induces two different monoids, (S, +, zero) and (S, *, one), where you “forget“ one of the operations. The former is even a commutative monoid. Make two new types AdditiveMonoid<S: Semiring> and MultiplicativeMonoid<S: Semiring> that converts a semiring to its monoid form.

3.) Recall that a “commutative monoid” is a monoid A in which a <> b = b <> a for all a, b: A. Show that

structEndoS<A:CommutativeMonoid>{letcall:(A)->A}

is a semiring where + is given by pointwise <>-application of functions and * is given by function composition.

4.) A ring is a semiring that has the additional axiom that addition is invertible, i.e. there is a function inverse: (S) -> S such that a + inverse(a) = inverse(a) + a = S.zero for all a: S. Can you think of anything that form a ring? Does Bool form a ring?