finite-1.5.0.0: src/lib/Finite/Class.hs
-----------------------------------------------------------------------------
-- |
-- Module : Finite.Class
-- Maintainer : Felix Klein
--
-- 'Finite' main class decleration including generics support.
--
-----------------------------------------------------------------------------
{-# LANGUAGE
ConstraintKinds
, DefaultSignatures
, FlexibleContexts
, FlexibleInstances
, ImplicitParams
, LambdaCase
, MultiParamTypeClasses
, TypeOperators
, ScopedTypeVariables
, RankNTypes
, RequiredTypeArguments
, ViewPatterns
#-}
-----------------------------------------------------------------------------
module Finite.Class
( FiniteBounds
, Finite(..)
, GFinite(..)
, withBounds
) where
-----------------------------------------------------------------------------
import Control.Exception
( assert
)
import GHC.Generics
( Generic
, Rep
, (:*:)(..)
, (:+:)(..)
, U1(..)
, M1(..)
, K1(..)
, from
, to
)
import qualified Data.IntSet as S
( toList
, fromList
, fromAscList
, difference
)
-----------------------------------------------------------------------------
-- | A better looking constraint specifier.
type FiniteBounds b = (?bounds :: b)
-----------------------------------------------------------------------------
-- | A more ergonomic way to set the implicit parameter.
withBounds :: b -> (FiniteBounds b => c) -> c
withBounds b x = let ?bounds = b in x
-----------------------------------------------------------------------------
-- | The 'Finite' class.
class Finite b a where
-- | Returns the number of elements associated with the given type.
elements :: forall c -> (a ~ c, FiniteBounds b) => Int
default elements
:: (Generic a, GFinite b (Rep a)) =>
forall c -> (a ~ c, FiniteBounds b) => Int
elements _ = gelements (Rep a)
-- | Turns the value in the associated range into an Int uniquely
-- identifiying the value.
index :: FiniteBounds b => a -> Int
default index ::
(Generic a, GFinite b (Rep a), FiniteBounds b) =>
a -> Int
index v = (+ (offset a)) $ gindex $ from v
-- | Turns an Int back to the value that is associated with it.
value :: FiniteBounds b => Int -> a
default value ::
(Generic a, GFinite b (Rep a), FiniteBounds b) => Int -> a
value v = assert (v >= o && v < o + e) $ to $ gvalue (v - o)
where
o = offset a
e = elements a
-- | Allows to put an offset to the integer mapping. Per default the
-- offset is zero.
offset :: forall c -> (a ~ c, FiniteBounds b) => Int
offset _ = 0
-- | Returns a finite list of all elements of that type.
values :: FiniteBounds b => [a]
values = value <$> [o, o + 1 .. o + n - 1]
where
n = elements a
o = offset a
-- | Complements a given list of elements of that type
complement :: FiniteBounds b => [a] -> [a]
complement xs = value <$> ys
where
o = offset a
n = elements a
s = S.fromList $ map index xs
as = S.fromAscList [o, o + 1 .. o + n - 1]
ys = S.toList $ S.difference as s
-- | Less than operator according to the implicit total index order.
(|<|) :: FiniteBounds b => a -> a -> Bool
x |<| y = index x < index y
infixr |<|
-- | Less or equal than operator according to the implicit total
-- index order.
(|<=|) :: FiniteBounds b => a -> a -> Bool
x |<=| y = index x <= index y
infixr |<=|
-- | Greater or equal than operator according to the implicit total
-- index order.
(|>=|) :: FiniteBounds b => a -> a -> Bool
x |>=| y = index x >= index y
infixr |>=|
-- | Greater than operator according to the implicit total index order.
(|>|) :: FiniteBounds b => a -> a -> Bool
x |>| y = index x > index y
infixr |>|
-- | Equal operator according to the implicit total index order.
(|==|) :: FiniteBounds b => a -> a -> Bool
x |==| y = index x == index y
infixr |==|
-- | Unequal operator according to the implicit total index order.
(|/=|) :: FiniteBounds b => a -> a -> Bool
x |/=| y = index x /= index y
infixr |/=|
-- | First element according to the total index order.
initial :: forall c -> (a ~ c, FiniteBounds b) => a
initial x = value $ offset x
-- | Last element according to the total index order.
final :: forall c -> (a ~ c, FiniteBounds b) => a
final x = value $ offset x + elements x - 1
-- | Next element according to the total index order (undefined for
-- the last element).
next :: FiniteBounds b => a -> a
next (index -> i) = assert (i < offset a + elements a - 1) $ value (i + 1)
-- | Previous element according to the total index order (undefined
-- for the first element).
previous :: FiniteBounds b => a -> a
previous (index -> i) = assert (i > offset a) $ value (i - 1)
-- | The upper and lower bounds of the instance.
bounds :: forall c -> (c ~ a, FiniteBounds b) => (a, a)
bounds x = (initial x, final x)
-----------------------------------------------------------------------------
-- | Generics implementation for the 'Finite' class. The
-- realization is closely related to the one presented at
-- https://wiki.haskell.org/GHC.Generics.
class GFinite b f where
gelements :: forall c -> (c ~ f, FiniteBounds b) => Int
gindex :: FiniteBounds b => f a -> Int
gvalue :: FiniteBounds b => Int -> f a
-----------------------------------------------------------------------------
-- | :*: instance.
instance (GFinite b f, GFinite b g) => GFinite b (f :*: g) where
gelements _ = gelements f * gelements g
gindex (f :*: g) = gindex f * gelements (type g) + gindex g
gvalue n = f :*: g
where
m = gelements (type g)
f = gvalue (n `div` m)
g = gvalue (n `mod` m)
-----------------------------------------------------------------------------
-- | :+: instance.
instance (GFinite b f, GFinite b g) => GFinite b (f :+: g) where
gelements _ = gelements f + gelements g
gindex = \case
R1 x -> gindex x
L1 x -> gindex x + gelements g
gvalue n
| n < m = R1 g
| otherwise = L1 f
where
m = gelements (type g)
g = gvalue (n `mod` m)
f = gvalue (n - m)
-----------------------------------------------------------------------------
-- | U1 instance.
instance GFinite c U1 where
gelements _ = 1
gindex U1 = 0
gvalue _ = U1
-----------------------------------------------------------------------------
-- | M1 instance.
instance GFinite c f => GFinite c (M1 i v f) where
gelements _ = gelements f
gindex (M1 x) = gindex x
gvalue = M1 . gvalue
-----------------------------------------------------------------------------
-- | K1 instance.
instance Finite b a => GFinite b (K1 i a) where
gelements _ = elements a
gindex (K1 x) = index x - offset a
gvalue n = K1 $ value (n + offset a)
-----------------------------------------------------------------------------