I recently released the Haskell library
world-peace-22.214.171.124. This library provides open sum types.
world-peace is not as fast as some other libraries providing open sum types, but it does have much better documentation than other libraries.
In this article I answer the following questions:
- What are open sum types?
- When would you want to use open sum types?
- When would you want to use
world-peaceinstead of other libraries?
What are open sum types?
Open sum types are used to represent "extensible" sum types. The opposite is a "closed" sum type. An example of a "closed" sum type in Haskell is
Imagine we have a
readFile function, which tries to read a file and return the contents:
FileReadErr might be a sum type that has multiple data constructors:
FileReadErrDoesNotExist when the file doesn't exist, and
FileReadErrPermissions when the file exists but the permissions are too strict for it to be read.
We could write something similar using open sum types to represent the different cases of
FileReadErr. This uses the
Since the type signature for
readFileOpenUnions is longer than
readFile, this looks significantly more complicated. Using type-level lists also make this more difficult. However, using open sum types gives us more flexibility in some situations.
When would you want to use open sum types?
Open sum types track the cases in the type system:
readFileResult can either be a value of
PermissionsErr. This is exactly the same as the following:
However, open sum types are more flexible because it is easy to add cases:
If we wanted to represent this with an
Either, it would start to look pretty messy:
At this point, most people would create a new datatype for holding these three cases:
This works really well in many situations. It is simple.
However, it is not flexible. If you wanted to add another error type, you'd need to add another constructor. If you wanted to handle just one of the error types (and keep the others), you'd need a new data type holding the remaining errors.
Open sum types make it easy to handle just one case:
openUnionRemove to peel off just the
PermissionsErr, resulting in an
OpenUnion with just two cases:
OpenUnion '[FileDoesNotExist, ErrorReadingFile].
It is also possible to make this function polymorphic. The body of the function is the same as for
handlePermsErr, but the type is a little more general.
handlePermsErrPolymorphic takes any
OpenUnion that contains a
PermissionsErr. It returns an
OpenUnion with all the original cases, but without the
PermissionsErr. This is quite convenient.
It is also easy to add a new error type.
Open sum types are nice to use when you want flexibility in constructing and handling multiple error types.
Other functionality for
OpenUnion can be found in the haddocks.
When to use world-peace vs. other libraries
There are many other libraries in the Haskell ecosystem that provide open sum types. Here are just a few:
The big advantage over these libraries is that world-peace has really good documentation. It should be easy to figure out how to use. There are many, many examples in the haddocks.
The main disadvantage of world-peace is that is it not as fast as libraries like
fastsum because of the way it represents open sum types internally1.
If you're thinking of using open sum types, I'd recommend the following:
If you're a beginner, I recommend you stick to normal ("closed") sum types like
If you're an intermediate Haskeller and want to play around with open sum types, I recommend world-peace because of its documentation.
If you're looking to use open sum types in an actual application, I recommend starting with world-peace because of its documentation. If you find that performance is a problem, I recommend switching to a faster library like
fastsum. These libraries should be much easier to use if you are already familiar with world-peace.
Real-world uses of world-peace
world-peace is used extensively in the library
servant-checked-exceptions uses open sum types to represent errors returned by Servant APIs. Using open sum types makes it easy to specify which APIs return which errors.
Other terms and other programming languages
There are many different terms used to describe "open sum types". Here are just a few:
- open unions
- extensible sum types
- polymorphic variants
If you want to read more about open sum types, I recommend you google some of these terms.
PureScript has extensible row types. These make it natural to define open sum types (called "polymorphic variants" here too).
Why the name "world-peace"?
All the other good package names were taken :-)
Writing extensive documentation is quite time-consuming. However, I think it is a big help for people using libraries that make use of Haskell's advanced type-level functionality.
Having examples for each function makes the library much easier to understand and use. I hope more Haskell libraries adopt this approach.
I posted this on Reddit. There are a couple good comments you may want to check out.