simple-enumeration-0.3: Finite or countably infinite sequences of values.
CopyrightBrent Yorgey Koji Miyazato
Maintainerbyorgey@gmail.com
Safe HaskellNone
LanguageHaskell2010

Data.ProEnumeration

Description

A proenumeration is a pair of a CoEnumeration and an Enumeration sharing the same cardinality.

A proenumeration can be seen as a function with an explicitly enumerated range.

Through documentations of this module, these import aliases are used:

import qualified Data.Enumeration as E
import qualified Data.CoEnumeration as C
Synopsis

Proenumeration type

data ProEnumeration a b Source #

A proenumeration is a pair of a CoEnumeration and an Enumeration sharing the same cardinality. Alternatively, a proenumeration can be seen as a function with an explicitly enumerated range.

Through this documentation, proenumerations are shown in diagrams of the following shape:

   f      g
a ---> N ---> b  :: ProEnumeration a b

Which means it is a value p :: ProEnumeration a b with cardinality N, locate p = f, and select p = g.

We can see N in the diagram as a subset of integers:

N = {i :: Integer | i < N}

Then it is actually a (category-theoretic) diagram showing values of ProEnumeration a b.

Instances

Instances details
Functor (ProEnumeration a) Source # 
Instance details

Defined in Data.ProEnumeration

Methods

fmap :: (a0 -> b) -> ProEnumeration a a0 -> ProEnumeration a b #

(<$) :: a0 -> ProEnumeration a b -> ProEnumeration a a0 #

card :: ProEnumeration a b -> Cardinality Source #

Get the cardinality of a proenumeration

isFinite :: ProEnumeration a b -> Bool Source #

Returns if the the cardinality of a proenumeration is finite.

baseEnum :: ProEnumeration a b -> Enumeration b Source #

Take an Enumeration from a proenumeration, discarding the CoEnumeration part

baseCoEnum :: ProEnumeration a b -> CoEnumeration a Source #

Take an CoEnumeration from a proenumeration, discarding Enumeration part

run :: ProEnumeration a b -> a -> b Source #

Turn a proenumeration into a normal function.

run p = (select p :: Index -> b) . (locate p :: a -> Index)

enumerateRange :: ProEnumeration a b -> [b] Source #

enumerateRange = E.enumerate . baseEnum

unsafeMkProEnumeration :: Cardinality -> (Index -> b) -> (a -> Index) -> ProEnumeration a b Source #

Constructs a proenumeration.

To construct a valid proenumeration by unsafeMkProEnumeration n f g, it must satisfy the following conditions:

  • For all i :: Integer, if 0 <= i && i < n, then f i should be "valid" (usually, it means f i should evaluate without exception).
  • For all x :: a, (Finite (g x) < n).

This functions does not (and never could) check the validity of its arguments.

mkProEnumeration :: CoEnumeration a -> Enumeration b -> ProEnumeration a b Source #

Constructs a proenumeration from a CoEnumeration and an Enumeration.

The cardinalities of the two arguments must be equal. Otherwise, mkProEnumeration returns an error.

baseEnum (mkProEnumeration a b) = b
baseCoEnum (mkProEnumeration a b) = a
>>> p = mkProEnumeration (C.modulo 3) (E.finiteList "abc")
>>> (card p, locate p 4, select p 1)
(Finite 3,1,'b')

ProEnumeration is a Profunctor

dimap :: (a' -> a) -> (b -> b') -> ProEnumeration a b -> ProEnumeration a' b' Source #

ProEnumeration is a Profunctor

dimap l r p = l .@ p @. r

(.@) :: (a' -> a) -> ProEnumeration a b -> ProEnumeration a' b infixr 8 Source #

l .@ p = dimap l id p

(@.) :: ProEnumeration a b -> (b -> b') -> ProEnumeration a b' infixl 7 Source #

p @. r = dimap id r p

Using Cardinality

data Cardinality Source #

The cardinality of a countable set: either a specific finite natural number, or countably infinite.

Constructors

Finite !Integer 
Infinite 

Instances

Instances details
Num Cardinality Source #

Cardinality has a Num instance for convenience, so we can use numeric literals as finite cardinalities, and add, subtract, and multiply cardinalities. Note that:

  • subtraction is saturating (i.e. 3 - 5 = 0)
  • infinity - infinity is treated as zero
  • zero is treated as a "very strong" annihilator for multiplication: even infinity * zero = zero.
Instance details

Defined in Data.Enumeration

Show Cardinality Source # 
Instance details

Defined in Data.Enumeration

Eq Cardinality Source # 
Instance details

Defined in Data.Enumeration

Ord Cardinality Source # 
Instance details

Defined in Data.Enumeration

type Index = Integer Source #

An index into an enumeration.

Primitive proenumerations

singleton :: b -> ProEnumeration a b Source #

singleton b = b <$ unit = mkProEnumeration C.unit (E.singleton b)

modulo :: Integer -> ProEnumeration Integer Integer Source #

modulo k = mkProEnumeration (C.modulo k) (E.finite k)
>>> card (modulo 13) == Finite 13
True
>>> run (modulo 13) 1462325 == 1462325 `mod` 13
True

clamped :: Integer -> Integer -> ProEnumeration Integer Integer Source #

clamped lo hi is a proenumeration of integers, which does not modify integers between lo and hi, inclusive, and limits smaller (larger) integer to lo (hi).

It is an error to call this function if lo > hi.

run (clamped lo hi) = min hi . max lo
>>> card (clamped (-2) 2)
Finite 5
>>> enumerateRange (clamped (-2) 2)
[-2,-1,0,1,2]
>>> run (clamped (-2) 2) <$> [-4 .. 4]
[-2,-2,-2,-1,0,1,2,2,2]

boundsChecked :: Integer -> Integer -> ProEnumeration Integer (Maybe Integer) Source #

boundsChecked lo hi is a proenumeration of a "bounds check" function, which validates that an input is between lo and hi, inclusive, and returns Nothing if it is outside those bounds.

run (boundsChecked lo hi) i

| lo <= i && i <= hi = Just i | otherwise = Nothing

>>> card (boundsChecked (-2) 2)
Finite 6
>>> -- Justs of [-2 .. 2] and Nothing
>>> enumerateRange (boundsChecked (-2) 2)
[Just (-2),Just (-1),Just 0,Just 1,Just 2,Nothing]
>>> run (boundsChecked (-2) 2) <$> [-4 .. 4]
[Nothing,Nothing,Just (-2),Just (-1),Just 0,Just 1,Just 2,Nothing,Nothing]

finiteList :: [a] -> ProEnumeration Integer (Maybe a) Source #

finiteList as is a proenumeration of a "bounds checked" indexing of as.

run (finiteList as) i

| 0 <= i && i < length as = Just (as !! i) | otherwise = Nothing

Note that finiteList uses !! on the input list under the hood, which has bad performance for long lists. See also the documentation of Data.Enumeration.finiteList. >>> card (finiteList HELLO) Finite 6 >>> -- Justs and Nothing >>> enumerateRange (finiteList HELLO) [Just H,Just E,Just L,Just L,Just O,Nothing] >>> run (finiteList HELLO) $ [0 .. 6] [Just H,Just E,Just L,Just L,Just O,Nothing,Nothing]

finiteCycle :: [a] -> ProEnumeration Integer a Source #

finiteCycle as is a proenumeration of an indexing of as, where every integer is a valid index by taking it modulo length as.

run (finiteCycle as) i = as !! (i `mod` length as)

If as is an empty list, it is an error.

>>> card (finiteCycle "HELLO")
Finite 5
>>> enumerateRange (finiteCycle "HELLO")
"HELLO"
>>> run (finiteCycle "HELLO") <$> [0 .. 10]
"HELLOHELLOH"

Combinators

infinite :: ProEnumeration a b -> ProEnumeration a b Source #

Sets the cardinality of given proenumeration to Infinite

compose :: ProEnumeration a b -> ProEnumeration b c -> ProEnumeration a c Source #

From two proenumerations p, q, we can make a proenumeration compose p q which behaves as a composed function (in diagrammatic order like >>>.)

run (compose p q) = run q . run p

p and q can be drawn in a diagram as follows:

[_______p______] [______q______]

   lp      sp      lq      sq
a ----> N ----> b ----> M ----> c

To get a proenumeration a -> ?? -> c, there are two obvious choices:

      run p >>> lq         sq
a --------------------> M ----> c
   lp         sp >>> run q
a ----> N --------------------> c

This function chooses the option with the smaller cardinality.

(><) :: ProEnumeration a1 b1 -> ProEnumeration a2 b2 -> ProEnumeration (a1, a2) (b1, b2) Source #

Cartesian product of proenumerations.

p >= 'mkProEnumeration' (baseCoEnum p C.'C.<' baseCoEnum q)
                            (baseEnum p   E.>< baseEnum q)

This operation is not associative if and only if one of the arguments is not finite.

(<+>) :: ProEnumeration a1 b1 -> ProEnumeration a2 b2 -> ProEnumeration (Either a1 a2) (Either b1 b2) Source #

Disjoint sum of proenumerations.

p + q = mkProEnumeration
   (baseCoEnum p C.<+>        baseCoEnum q)
   (baseEnum p   `E.eitherOf' baseEnum q)

This operation is not associative if and only if one of the arguments is not finite.

maybeOf :: ProEnumeration a b -> ProEnumeration (Maybe a) (Maybe b) Source #

maybeOf p = mkProEnumeration (C.maybeOf (baseCoEnum p)) (E.maybeOf (baseEnum p))

eitherOf :: ProEnumeration a1 b1 -> ProEnumeration a2 b2 -> ProEnumeration (Either a1 a2) (Either b1 b2) Source #

Synonym of (<+>)

listOf :: ProEnumeration a b -> ProEnumeration [a] [b] Source #

listOf p = mkProEnumeration (C.listOf (baseCoEnum p)) (E.listOf (baseEnum p))

finiteSubsetOf :: ProEnumeration a b -> ProEnumeration [a] [b] Source #

finiteSubsetOf p = mkProEnumeration
    (C.finiteSubsetOf (baseCoEnum p))
    (E.finiteSubsetOf (baseEnum p))

enumerateP :: CoEnumeration a -> Enumeration b -> Enumeration (ProEnumeration a b) Source #

Enumerate every possible proenumeration.

enumerateP a b generates proenumerations p such that the function run p has the following properties:

  • The range of run p is a subset of b :: Enumeration b.
  • If locate a x = locate a y, then run p x = run p y. In other words, run p factors through locate a.

This function generates proenumerations p in such a way that every function of type a -> b with the above properties uniquely appears as run p for some enumerated p.

coenumerateP :: Enumeration a -> CoEnumeration b -> CoEnumeration (a -> b) Source #

Coenumerate every possible function.

coenumerateP as bs classifies functions of type a -> b by the following criterion:

f and g have the same index

if and only if

For all elements a of as :: Enumeration a, locate bs (f a) = locate bs (g a).

Note: The suffix P suggests it coenumerates ProEnumeration a b, but it actually coenumerates a -> b. The reason is that ProEnumeration a b carries extra data and constraints like its cardinality, but the classification does not care about those. Thus, it is more permissive to accept any function of type a -> b.

To force it to coenumerate proenumerations, contramap run can be applied.

proenumerationOf :: ProEnumeration a a' -> ProEnumeration b b' -> ProEnumeration (a' -> b) (ProEnumeration a b') Source #

enumerateP and coenumerateP combined.

   l_a      s_a
a -----> N -----> a'  :: ProEnumeration a a'

   l_b      s_b
b -----> M -----> b'  :: ProEnumeration b b'


(N -> b) ---> (N -> M) ---> (N -> b')
   ^             ||             |
   | (. s_a)     ||             | (. l_a)
   |             ||             v
(a' -> b)      (M ^ N)       (a -> b')
  • When N is finite, (M ^ N) is at most countable, since M is at most countable.
  • The enumerated functions (of type a -> b') are compositions of l_a :: a -> N and functions of type N -> b. It is beneficial to tell this fact by the type, which happens to be ProEnumeration a b'.

finiteFunctionOf :: Integer -> ProEnumeration a b -> ProEnumeration (Integer -> a) (Integer -> b) Source #

finiteFunctionOf k p is a proenumeration of products of k copies of a and b respectively.

If p is a invertible enumeration, finiteFunctionOf k p is too.

It is implemented using proenumerationOf.