finitary
A better, more type-safe Enum.
https://codeberg.org/sheaf/finitary
| Stackage Nightly 2026-04-19: | 2.2.0.1 |
| Latest on Hackage: | 2.2.0.1 |
finitary-2.2.0.1@sha256:add2c2a2898bce62114a0de05cc812f35cf16eecc2289101aa7f7445d958d961,2734Module documentation for 2.2.0.1
- Data
finitary
What’s all this about?
finitary allows us to specify that a type is finite (that is, contains
finitely many inhabitants which are not _|_), and have confirmation of this
fact by GHC. Additionally, it offers a Generics-based auto-derivation
interface for this, as well as multiple helper functions that are enabled by all
this machinery.
Why is this a big deal?
Consider Enum. It’s not difficult to see that Enum has issues:
It’s partial all over the place
What will this code do?
toEnum 3 :: Bool
The answer is ‘a runtime error’. How about this?
succ True
The answer, again, is ‘a runtime error’. Many of the methods provided by Enum
are partial like this, because many types that happen to be Enum instances
have cardinalities (much) smaller than Int, which necessitates leaving some
Int values ‘out’.
The converse is not much better: on some platforms, Int has smaller
cardinality than some types with Enum instances in base. For example, on
a platform where Int is 32 bits wide, the Word64 instance will definitely
cause problems, as it’s ‘too big’.
It gives us almost no information
An Enum instance says that a type can be munged to and from an Int…
somehow. While base and the Haskell Report certainly provide some limits
on its behaviour, a lot of questions remain unanswered, including:
- How many inhabitants does this type have?
- What are the ‘safe’ values of
IntI can feed totoEnum? - For any
x, istoEnum . (+ 1) . fromEnum $ xsafe (in that it’ll give us a value instead of blowing up)?
We don’t have a (default) way to auto-derive it
Quoting base:
Instances of
Enummay be derived for any enumeration type (types whose constructors have no fields).
But what if your type has fields, especially when they’re instances of Enum?
Unfortunately, no auto-derivation for you. While this stance makes some sense,
it’s still rather inconvenient.
OK, so what are you offering instead?
The core of finitary is the Finitary type class. If we have an instance
of Finitary for some type a, we have a witness to an isomorphism between
a and some (KnownNat n) => Finite n. More precisely, we (together with GHC)
know:
- That
ahas finitely-many non-_|_inhabitants - The value of
n, which is the cardinality ofa(how many inhabitants we have exactly) - Two functions to ‘witness’ the isomorphism, namely
fromFinite :: Finite n -> aandtoFinite :: a -> Finite n
How does Finitary solve the issues behind Enum?
Everything is total, forever
There is no way to call fromFinite or toFinite with an ‘inappropriate’
argument. We always know - if you give me a Finite n, I will give you back a
(unique) a, guaranteed.
We learn a lot from a type having a Finitary instance
Aside from cardinality, we also inherently get the ability to:
- Have a ‘starting’ and ‘ending’ value (assuming the cardinality of the type isn’t zero); and
- Get the ‘next’ or ‘previous’ value, or report that it doesn’t exist.
All of this is safe, total and can be relied upon. Check out the documentation
for more details - all of this functionality is provided. We also have functions
to help enumerate values of Finitary types.
But what about auto-derivation?
We have you covered. If you want to auto-derive an instance of
Finitary for your type, you absolutely can, using the power of
GHC.Generics:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
import Data.Finitary (Finitary)
import Data.Vector.Sized (Vector)
import Data.Word (Word8)
import GHC.Generics (Generic)
data Foo = Bar | Baz (Word8, Word8) | Quux (Vector 4 Bool)
deriving stock (Eq, Generic)
deriving anyclass (Finitary)
Furthermore, GHC will even calculate the cardinality for you. To assist in this,
we have provided as many instances of Finitary for ‘base’ types as possible -
see the documentation for full details.
That all seems rather cool - what else can I do with this?
Knowing that a type has finite cardinality is usable for many things - all of which we plan to provide. Some examples (with links once we have working, tested code) include:
- Automatic derivation of instances
- Type-safe refinement
- Random generation and stream sampling
- Efficient sets, allowing operations like complements and a
Monoidunder intersection - Efficient maps
- Various clever
lenstricks
If there’s something else interesting you think can be done with this, let us know: it might make it onto this list, and into code.
License
This library is under the GNU General Public License, version 3 or later (SPDX
code GPL-3.0-or-later). For more details, see the LICENSE.md file.
Changes
Revision history for finitary
2.2.0.1 – 2026-04-18
-
Relax upper bounds:
ghc-typelits-knownnat:< 0.8 ==> < 0.9ghc-typelits-natnormalise:< 0.8 ==> < 0.10
-
Update the testsuite to account for changes in
hspec-hedgehog.
2.2.0.0 – 2024-08-07
-
Fix behaviour of
previousandnext, which incorrectly handled endpoints. Thanks toblmagefor their contribution. -
Relax upper bounds:
primitive:< 0.8 ==> < 0.10vector:< 0.13 ==> < 0.14vector-sized:< 1.6 ==> < 1.7
2.1.3.0 – 2024-05-09
- Add support for
finite-typelits >= 2.0.0.
2.1.2.0 – 2024-05-06
- Allow
typelits-witnesses-0.4.0.1, and bump upper bounds ofhspecandhspec-hedgehogin the test component (thanks to viercc).
2.1.1.1 – 2022-12-03
- Bump some upper bounds, including
base.
2.1.1.0 – 2021-02-11
- Work around a bug in
fromIntegral :: Natural -> Integerin GHC 9.0 (GHC issue #19345).
2.1.0.1 – 2021-02-09
- Fix incorrect instance for
Finite a => Finite ( Down a ) - Add cabal flags to give the option of removing dependencies on
vectorandbitvec - Ensure GHC 9.0 compatibility
2.0.0.0 – 2020-05-05
- Support GHC 8.10
- Remove support for GHCs older than 8.6
- Remove dependency on
coercible-utils previousandnextnow returnMaybes- Better tests
- Better documentation
1.2.0.0 – 2019-10-17
- Remove
nextSkippingandpreviousSkippingas unnecessary. - Add (and note) support for GHC 8.2.2.
- Remove MTL dependency.
- Remove
enumerate*class methods. - Add
inhabitants,inhabitantsFrom,inhabitantsTo,inhabitantsFromTo.
1.1.0.0 – 2019-09-21
- Repair a disagreement between
OrdandFinitaryderivations viaGeneric(thanks jle`!). - Fix serious bug with
nextandprevious.
1.0.0.1 – 2019-09-17
- Fix README (no code changes).
1.0.0.0 – 2019-09-17
- Rewritten documentation to be more clear, and state more laws.
- Added
nextSkippingandpreviousSkippingmethods to allow ‘skipping over’ some elements when enumerating in a more efficient manner. - Added
Eq aas a constraint forFinitary ainstances (as without it, the laws don’t make much sense). - Fixed numerous typoes and unclear statements.
- Stated that
Finitarymust be order-preserving relative any instances withOrdinstances; existing instances modified to follow this law. - Added more tests.
- Fixed odd issue with some element cardinalities and lengths for sized
Vectorinstances. - Tightened bounds to be more conservative.
- Support GHC 8.8.
0.1.0.0 – 2019-09-10
- First version. Released on an unsuspecting world.