finitary-2.1.0.0: A better, more type-safe Enum.
Copyright (C) Koz Ross 2019-2020 GPL version 3.0 or later Experimental GHC only Trustworthy Haskell2010

Data.Finitary

Description

This package provides the Finitary type class, a range of useful 'base' instances for commonly-used finitary types, and some helper functions for enumerating values of types with Finitary instances.

For your own types, there are three possible ways to define an instance of Finitary:

Via Generic

If your data type implements Generic (and is finitary), you can automatically derive your instance:

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}

import Data.Finitary (Finitary)
import Data.Word (Word8, Word16)
import GHC.Generics (Generic)

data Foo = Bar | Baz (Word8, Word8) | Quux Word16
deriving stock (Eq, Generic)
deriving anyclass (Finitary)

This is the easiest method, and also the safest, as GHC will automatically determine the cardinality of Foo, as well as defining law-abiding methods. It may be somewhat slower than a 'hand-rolled' method in some cases.

By defining only Cardinality, fromFinite and toFinite

If you want a manually-defined instance, but don't wish to define every method, only fromFinite and toFinite are needed, along with Cardinality. Cardinality in particular must be defined with care, as otherwise, you may end up with inconstructable values or indexes that don't correspond to anything.

By defining everything

For maximum control, you can define all the methods. Ensure you follow all the laws!

Synopsis

# Documentation

class (Eq a, KnownNat (Cardinality a)) => Finitary (a :: Type) where Source #

Witnesses an isomorphism between a and some (KnownNat n) => Finite n. Effectively, a lawful instance of this shows that a has exactly n (non-_|_) inhabitants, and that we have a bijection with fromFinite and toFinite as each 'direction'.

For any type a with an instance of Finitary, for every non-_|_ x :: a, we have a unique index i :: Finite n. We will also refer to any such x as an inhabitant of a. We can convert inhabitants to indexes using toFinite, and also convert indexes to inhabitants with fromFinite.

Laws

The main laws state that fromFinite should be a bijection, with toFinite as its inverse, and Cardinality must be a truthful representation of the cardinality of the type. Thus:

• $\texttt{fromFinite} \circ \texttt{toFinite} = \texttt{toFinite} \circ \texttt{fromFinite} = \texttt{id}$
• $\forall x, y :: \texttt{Finite} \; (\texttt{Cardinality} \; a) \; \texttt{fromFinite} \; x = \texttt{fromFinite} \; y \rightarrow x = y$
• $\forall x :: \texttt{Finite} \; (\texttt{Cardinality} \; a) \; \exists y :: a \mid \texttt{fromFinite} \; x = y$

Furthermore, fromFinite should be _order-preserving_. Namely, if a is an instance of Ord, we must have:

• $\forall i, j :: \texttt{Finite} \; (\texttt{Cardinality} \; a) \; \texttt{fromFinite} \; i \leq \texttt{fromFinite} \; j \rightarrow i \leq j$

Lastly, if you define any of the other methods, these laws must hold:

• $a \neq \emptyset \rightarrow \texttt{start} :: a = \texttt{fromFinite} \; \texttt{minBound}$
• $a \neq \emptyset \rightarrow \texttt{end} :: a = \texttt{fromFinite} \; \texttt{maxBound}$
• $\forall x :: a \; \texttt{end} :: a \neq x \rightarrow \texttt{next} \; x = (\texttt{fromFinite} \circ + 1 \circ \texttt{toFinite}) \; x$
• $\forall x :: a \; \texttt{start} :: a \neq x \rightarrow \texttt{previous} \; x = (\texttt{fromFinite} \circ - 1 \circ \texttt{toFinite}) \; x$

Together with the fact that Finite n is well-ordered whenever KnownNat n holds, a law-abiding Finitary instance for a type a defines a constructive well-order, witnessed by toFinite and fromFinite, which agrees with the Ord instance for a, if any.

We strongly suggest that fromFinite and toFinite should have time complexity $$\Theta(1)$$, or, if that's not possible, $$O(\texttt{Cardinality} \; a)$$. The latter is the case for instances generated using Generics-based derivation, but not for 'basic' types; thus, these functions for your derived types will only be as slow as their 'structure', rather than their 'contents', provided the contents are of these 'basic' types.

Minimal complete definition

Nothing

Associated Types

type Cardinality a :: Nat Source #

How many (non-_|_) inhabitants a has, as a typelevel natural number.

type Cardinality a = GCardinality (Rep a)

Methods

fromFinite :: Finite (Cardinality a) -> a Source #

Converts an index into its corresponding inhabitant.

default fromFinite :: (Generic a, GFinitary (Rep a), Cardinality a ~ GCardinality (Rep a)) => Finite (Cardinality a) -> a Source #

toFinite :: a -> Finite (Cardinality a) Source #

Converts an inhabitant to its corresponding index.

default toFinite :: (Generic a, GFinitary (Rep a), Cardinality a ~ GCardinality (Rep a)) => a -> Finite (Cardinality a) Source #

start :: 1 <= Cardinality a => a Source #

The first inhabitant, by index, assuming a has any inhabitants.

end :: 1 <= Cardinality a => a Source #

The last inhabitant, by index, assuming a has any inhabitants.

previous :: a -> Maybe a Source #

previous x gives Just the inhabitant whose index precedes the index of x, or Nothing if no such index exists.

next :: a -> Maybe a Source #

next x gives Just the inhabitant whose index follows the index of x, or Nothing if no such index exists.

#### Instances

Instances details
 Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Char has one inhabitant per Unicode code point. Instance detailsDefined in Data.Finitary Associated Types Methods Source # Int has a finite number of inhabitants, varying by platform. This instance will determine this when the library is built. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality Int :: Nat Source # Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Word has a finite number of inhabitants, varying by platform. This instance will determine this when the library is built. Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Types Methods Finitary () Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality () :: Nat Source # MethodsfromFinite :: Finite (Cardinality ()) -> () Source #toFinite :: () -> Finite (Cardinality ()) Source #start :: () Source #end :: () Source #previous :: () -> Maybe () Source #next :: () -> Maybe () Source # Source # Instance detailsDefined in Data.Finitary Associated Types Methods Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality All :: Nat Source # Methods Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality Any :: Nat Source # Methods Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality Bit :: Nat Source # Methods Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality Bit :: Nat Source # Methods Finitary a => Finitary (Maybe a) Source # Maybe a introduces one additional inhabitant (namely, Nothing) to a. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Maybe a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Maybe a)) -> Maybe a Source #toFinite :: Maybe a -> Finite (Cardinality (Maybe a)) Source #previous :: Maybe a -> Maybe (Maybe a) Source #next :: Maybe a -> Maybe (Maybe a) Source # Finitary a => Finitary (Min a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Min a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Min a)) -> Min a Source #toFinite :: Min a -> Finite (Cardinality (Min a)) Source #previous :: Min a -> Maybe (Min a) Source #next :: Min a -> Maybe (Min a) Source # Finitary a => Finitary (Max a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Max a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Max a)) -> Max a Source #toFinite :: Max a -> Finite (Cardinality (Max a)) Source #previous :: Max a -> Maybe (Max a) Source #next :: Max a -> Maybe (Max a) Source # Finitary a => Finitary (First a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (First a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (First a)) -> First a Source #toFinite :: First a -> Finite (Cardinality (First a)) Source #previous :: First a -> Maybe (First a) Source #next :: First a -> Maybe (First a) Source # Finitary a => Finitary (Last a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Last a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Last a)) -> Last a Source #toFinite :: Last a -> Finite (Cardinality (Last a)) Source #previous :: Last a -> Maybe (Last a) Source #next :: Last a -> Maybe (Last a) Source # Finitary a => Finitary (Identity a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Identity a) :: Nat Source # Methodsprevious :: Identity a -> Maybe (Identity a) Source #next :: Identity a -> Maybe (Identity a) Source # Finitary a => Finitary (Dual a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Dual a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Dual a)) -> Dual a Source #toFinite :: Dual a -> Finite (Cardinality (Dual a)) Source #previous :: Dual a -> Maybe (Dual a) Source #next :: Dual a -> Maybe (Dual a) Source # Finitary a => Finitary (Sum a) Source # For any newtype-esque thing over a type with a Finitary instance, we can just 'inherit' the behaviour of a. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Sum a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Sum a)) -> Sum a Source #toFinite :: Sum a -> Finite (Cardinality (Sum a)) Source #previous :: Sum a -> Maybe (Sum a) Source #next :: Sum a -> Maybe (Sum a) Source # Finitary a => Finitary (Product a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Product a) :: Nat Source # Methodsprevious :: Product a -> Maybe (Product a) Source #next :: Product a -> Maybe (Product a) Source # Finitary a => Finitary (Down a) Source # Despite the newtype-esque nature of Down, due to the requirement that fromFinite is order-preserving, the instance for Down a reverses the indexing. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Down a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Down a)) -> Down a Source #toFinite :: Down a -> Finite (Cardinality (Down a)) Source #previous :: Down a -> Maybe (Down a) Source #next :: Down a -> Maybe (Down a) Source # KnownNat n => Finitary (Finite n) Source # Since any type is isomorphic to itself, it follows that a 'valid' Finite n (meaning that n is a KnownNat) has finite cardinality. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Finite n) :: Nat Source # MethodstoFinite :: Finite n -> Finite (Cardinality (Finite n)) Source #previous :: Finite n -> Maybe (Finite n) Source #next :: Finite n -> Maybe (Finite n) Source # (Finitary a, Finitary b) => Finitary (Either a b) Source # The sum of two finite types will also be finite, with a cardinality equal to the sum of their cardinalities. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Either a b) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Either a b)) -> Either a b Source #toFinite :: Either a b -> Finite (Cardinality (Either a b)) Source #start :: Either a b Source #end :: Either a b Source #previous :: Either a b -> Maybe (Either a b) Source #next :: Either a b -> Maybe (Either a b) Source # (Finitary a, Finitary b) => Finitary (a, b) Source # The product of two finite types will also be finite, with a cardinality equal to the product of their cardinalities. Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (a, b) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (a, b)) -> (a, b) Source #toFinite :: (a, b) -> Finite (Cardinality (a, b)) Source #start :: (a, b) Source #end :: (a, b) Source #previous :: (a, b) -> Maybe (a, b) Source #next :: (a, b) -> Maybe (a, b) Source # Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Proxy a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Proxy a)) -> Proxy a Source #toFinite :: Proxy a -> Finite (Cardinality (Proxy a)) Source #previous :: Proxy a -> Maybe (Proxy a) Source #next :: Proxy a -> Maybe (Proxy a) Source # (Finitary a, Unbox a, KnownNat n) => Finitary (Vector n a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Vector n a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Vector n a)) -> Vector n a Source #toFinite :: Vector n a -> Finite (Cardinality (Vector n a)) Source #start :: Vector n a Source #end :: Vector n a Source #previous :: Vector n a -> Maybe (Vector n a) Source #next :: Vector n a -> Maybe (Vector n a) Source # (Finitary a, Storable a, KnownNat n) => Finitary (Vector n a) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Vector n a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Vector n a)) -> Vector n a Source #toFinite :: Vector n a -> Finite (Cardinality (Vector n a)) Source #start :: Vector n a Source #end :: Vector n a Source #previous :: Vector n a -> Maybe (Vector n a) Source #next :: Vector n a -> Maybe (Vector n a) Source # (Finitary a, KnownNat n) => Finitary (Vector n a) Source # A fixed-length vector over a type a with an instance of Finitary can be thought of as a fixed-length word over an alphabet of size Cardinality a. Since there are only finitely-many of these, we can index them in lex order, with the ordering determined by the Finitary a instance (thus, the 'first' such Vector is the one where each element is start :: a, and the 'last' is the one where each element is end :: a). Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Vector n a) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Vector n a)) -> Vector n a Source #toFinite :: Vector n a -> Finite (Cardinality (Vector n a)) Source #start :: Vector n a Source #end :: Vector n a Source #previous :: Vector n a -> Maybe (Vector n a) Source #next :: Vector n a -> Maybe (Vector n a) Source # (Finitary a, Finitary b, Finitary c) => Finitary (a, b, c) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (a, b, c) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (a, b, c)) -> (a, b, c) Source #toFinite :: (a, b, c) -> Finite (Cardinality (a, b, c)) Source #start :: (a, b, c) Source #end :: (a, b, c) Source #previous :: (a, b, c) -> Maybe (a, b, c) Source #next :: (a, b, c) -> Maybe (a, b, c) Source # Finitary a => Finitary (Const a b) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (Const a b) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (Const a b)) -> Const a b Source #toFinite :: Const a b -> Finite (Cardinality (Const a b)) Source #start :: Const a b Source #end :: Const a b Source #previous :: Const a b -> Maybe (Const a b) Source #next :: Const a b -> Maybe (Const a b) Source # (Finitary a, Finitary b, Finitary c, Finitary d) => Finitary (a, b, c, d) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (a, b, c, d) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (a, b, c, d)) -> (a, b, c, d) Source #toFinite :: (a, b, c, d) -> Finite (Cardinality (a, b, c, d)) Source #start :: (a, b, c, d) Source #end :: (a, b, c, d) Source #previous :: (a, b, c, d) -> Maybe (a, b, c, d) Source #next :: (a, b, c, d) -> Maybe (a, b, c, d) Source # (Finitary a, Finitary b, Finitary c, Finitary d, Finitary e) => Finitary (a, b, c, d, e) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (a, b, c, d, e) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (a, b, c, d, e)) -> (a, b, c, d, e) Source #toFinite :: (a, b, c, d, e) -> Finite (Cardinality (a, b, c, d, e)) Source #start :: (a, b, c, d, e) Source #end :: (a, b, c, d, e) Source #previous :: (a, b, c, d, e) -> Maybe (a, b, c, d, e) Source #next :: (a, b, c, d, e) -> Maybe (a, b, c, d, e) Source # (Finitary a, Finitary b, Finitary c, Finitary d, Finitary e, Finitary f) => Finitary (a, b, c, d, e, f) Source # Instance detailsDefined in Data.Finitary Associated Typestype Cardinality (a, b, c, d, e, f) :: Nat Source # MethodsfromFinite :: Finite (Cardinality (a, b, c, d, e, f)) -> (a, b, c, d, e, f) Source #toFinite :: (a, b, c, d, e, f) -> Finite (Cardinality (a, b, c, d, e, f)) Source #start :: (a, b, c, d, e, f) Source #end :: (a, b, c, d, e, f) Source #previous :: (a, b, c, d, e, f) -> Maybe (a, b, c, d, e, f) Source #next :: (a, b, c, d, e, f) -> Maybe (a, b, c, d, e, f) Source #

# Enumeration functions

inhabitants :: forall (a :: Type). Finitary a => [a] Source #

Produce every inhabitant of a, in ascending order of indexes. If you want descending order, use Down a instead.

inhabitantsFrom :: forall (a :: Type). Finitary a => a -> NonEmpty a Source #

Produce every inhabitant of a, starting with the argument, in ascending order of indexes. If you want descending order, use Down a instead.

inhabitantsTo :: forall (a :: Type). Finitary a => a -> NonEmpty a Source #

Produce every inhabitant of a, up to and including the argument, in ascending order of indexes. If you want descending order, use Down a instead.

inhabitantsFromTo :: forall (a :: Type). Finitary a => a -> a -> [a] Source #

Produce every inhabitant of a, starting with the first argument, up to the second argument, in ascending order of indexes. inhabitantsFromTo x y will produce the empty list if toFinite x > toFinite y. If you want descending order, use Down a instead.