generic-enum-0.1.1.0: src/Data/Generic/Enum.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module Data.Generic.Enum (
Enum(EnumNumT, EnumIntegralT, succ, pred, toEnum, fromEnum, numStepsBetween),
DefaultEnum(defaultFromEnum, defaultToEnum),
Element,
EnumFromTo(enumFromTo, enumFromThenTo, enumFromThenCount, enumFromCount, enumFromStepTo, enumFromStepCount),
EnumFrom(enumFrom, enumFromThen, enumFromStep),
) where
import qualified Prelude
import Prelude hiding (Enum, fromEnum, toEnum, succ, pred, enumFrom, enumFromThen, enumFromTo, enumFromThenTo)
import Data.Int
import Data.Word
import Data.Char
import System.IO
import GHC.Generics
import Foreign.C.Types
import Foreign.Ptr
import System.Posix.Types
import Numeric.Natural
import GHC.RTS.Flags
import Data.Ratio
import Data.Fixed
import Data.Semigroup
import Data.Functor.Identity
import Data.Proxy
import Data.Type.Equality
import Data.Coerce
import Data.Type.Coercion
import Data.Monoid (Alt(Alt), getAlt)
import Data.Functor.Const
import qualified GHC.Enum
import Data.Array (Array, listArray, Ix, elems)
import Data.List (genericLength)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Short as BSS
{-|
The generic 'Enum' class. Firstly, this class just deals with 'fromEnum', 'toEnum' type functions,
not the list generating functions like 'Prelude.enumFrom' and 'Prelude.enumFromTo'
the normal 'Prelude.Enum' has.
This class has a number of defaults for making defining both existing Prelude style Enum classes and
ordinary Numeric classes quick and painless.
Firstly, for existing Enums:
> instance Enum Blah
Will completely define @Blah@ as an @Enum@ if @Blah@ is already a Prelude style Enum, just forwarding
calls to the functions in the Prelude's Enum.
Secondly, for integral datatypes (i.e. in class 'Integral')
> instance Enum Blah
> type EnumNumT Blah = Blah
will defined @Blah@ to be an Enum, with it's Enum type itself.
For example,
> instance Enum Integer
> type EnumNumT Integer = Integer
is an Enum with 'fromEnum' and 'toEnum' simply 'Prelude.id'.
Note that with this approach, @toEnum . fromEnum == id@, instead of going through 'Int'
and possibly overflowing.
Note also that operations like 'succ' and 'pred' don't bounds check like the Prelude versions often do.
For types that don't fit one of the above two categories (i.e. don't have a satisfactory Prelude Enum
instance or aren't Integral) you'll have to define the individual functions as discussed with their documentation.
Note that the following function, whilst valid with Prelude style enums, is not valid with the 'Enum' class in this module:
> convertEnum :: (Enum a, Enum b) => a -> b
> convertEnum = toEnum . fromEnum
because now, Enum's can have different "enum types". That is. 'fromEnum' is not always an 'Int', and 'toEnum' does
not always take an 'Int'.
Though it is debatable if the above function is sensible though anyway.
I have attempted to define instances of 'Enum' for all types in GHCs included libraries, tell me
if I've missed any though.
-}
class (Num (EnumNumT a), Integral (EnumIntegralT a)) => Enum a where
{-|
This is the \"enum\" type. It just needs to be in the class 'Num'.
-}
type EnumNumT a
type EnumNumT a = Int
{-|
'EnumIntegralT' (default - 'EnumNumT'): this is a type that represents the number of \"steps\"
between two enums, based on a stepsize. Whilst 'EnumNumT' must only be a 'Num', 'EnumIntegralT'
needs to be 'Integral'. If 'EnumNumT' is already 'Integral' it's almost certainly a good choice.
-}
type EnumIntegralT a
type EnumIntegralT a = EnumNumT a
succ :: a -> a
succ = toEnum . (+ 1) . fromEnum
pred :: a -> a
pred = toEnum . (subtract 1) . fromEnum
{-| Just like Prelude's 'Prelude.toEnum', but with @EnumNumT t@ instead of 'Int' -}
toEnum :: EnumNumT a -> a
default toEnum :: (DefaultEnum a (EnumNumT a)) => EnumNumT a -> a
toEnum = defaultToEnum
{-| Just like Prelude's 'Prelude.fromEnum', but with @EnumNumT t@ instead of 'Int' -}
fromEnum :: a -> EnumNumT a
default fromEnum :: (DefaultEnum a (EnumNumT a)) => a -> EnumNumT a
fromEnum = defaultFromEnum
{-|
'numStepsBetween': This takes three arguments, firstly, two of type @t@ for some @Enum t@ (\"@start\"@ and \"@end\"@,
and also \"@step@\" of @EnumNumT t@, i.e. the \"enum\" type of @t@.
The result should be the length of the following list:
> [start, (start + step) .. end]
and also of type @EnumIntegralT t@. It should not be less than 0.
For example:
> numStepsBetween 'a' 'e' 2
should be 3.
-}
numStepsBetween :: a -> a -> EnumNumT a -> EnumIntegralT a
default numStepsBetween :: (e ~ EnumNumT a, e ~ EnumIntegralT a) => a -> a -> e -> e
numStepsBetween x y stepSize = max ((fromEnum y - fromEnum x) `div` stepSize + 1) 0
{-|
A little trick for defining the two default cases mentioned in the documentation for 'Enum'.
-}
class DefaultEnum a b where
defaultFromEnum :: a -> b
defaultToEnum :: b -> a
instance (Prelude.Enum a) => DefaultEnum a Int where
defaultFromEnum = Prelude.fromEnum
defaultToEnum = Prelude.toEnum
instance DefaultEnum a a where
defaultFromEnum = id
defaultToEnum = id
instance {-# OVERLAPPING #-} DefaultEnum Int Int where
defaultFromEnum = id
defaultToEnum = id
instance Enum Bool
instance Enum Char
instance Enum Int
instance Enum Int8 where
type EnumNumT Int8 = Int8
instance Enum Int16 where
type EnumNumT Int16 = Int16
instance Enum Int32 where
type EnumNumT Int32 = Int32
instance Enum Int64 where
type EnumNumT Int64 = Int64
instance Enum Integer where
type EnumNumT Integer = Integer
instance Enum Ordering
instance Enum Word8 where
type EnumNumT Word8 = Word8
instance Enum Word16 where
type EnumNumT Word16 = Word16
instance Enum Word32 where
type EnumNumT Word32 = Word32
instance Enum Word64 where
type EnumNumT Word64 = Word64
instance Enum ()
instance Enum GeneralCategory
instance Enum IOMode
instance Enum DecidedStrictness
instance Enum SourceStrictness
instance Enum SourceUnpackedness
instance Enum Associativity
instance Enum SeekMode
instance Enum CUIntMax where
type EnumNumT CUIntMax = CUIntMax
instance Enum CIntMax where
type EnumNumT CIntMax = CIntMax
instance Enum CUIntPtr where
type EnumNumT CUIntPtr = CUIntPtr
instance Enum CIntPtr where
type EnumNumT CIntPtr = CIntPtr
instance Enum CSUSeconds
instance Enum CUSeconds
instance Enum CTime
instance Enum CClock
instance Enum CSigAtomic where
type EnumNumT CSigAtomic = CSigAtomic
instance Enum CWchar where
type EnumNumT CWchar = CWchar
instance Enum CSize where
type EnumNumT CSize = CSize
instance Enum CPtrdiff where
type EnumNumT CPtrdiff = CPtrdiff
instance Enum CDouble
instance Enum CFloat
instance Enum CULLong where
type EnumNumT CULLong = CULLong
instance Enum CLLong where
type EnumNumT CLLong = CLLong
instance Enum CULong where
type EnumNumT CULong = CULong
instance Enum CLong where
type EnumNumT CLong = CLong
instance Enum CUInt where
type EnumNumT CUInt = CUInt
instance Enum CInt where
type EnumNumT CInt = CInt
instance Enum CUShort where
type EnumNumT CUShort = CUShort
instance Enum CShort where
type EnumNumT CShort = CShort
instance Enum CUChar where
type EnumNumT CUChar = CUChar
instance Enum CSChar where
type EnumNumT CSChar = CSChar
instance Enum CChar where
type EnumNumT CChar = CChar
instance Enum IntPtr where
type EnumNumT IntPtr = IntPtr
instance Enum WordPtr where
type EnumNumT WordPtr = WordPtr
instance Enum Fd where
type EnumNumT Fd = Fd
instance Enum CRLim where
type EnumNumT CRLim = CRLim
instance Enum CTcflag where
type EnumNumT CTcflag = CTcflag
instance Enum CSpeed
instance Enum CCc
instance Enum CUid where
type EnumNumT CUid = CUid
instance Enum CNlink where
type EnumNumT CNlink = CNlink
instance Enum CGid where
type EnumNumT CGid = CGid
instance Enum CSsize where
type EnumNumT CSsize = CSsize
instance Enum CPid where
type EnumNumT CPid = CPid
instance Enum COff where
type EnumNumT COff = COff
instance Enum CMode where
type EnumNumT CMode = CMode
instance Enum CIno where
type EnumNumT CIno = CIno
instance Enum CDev where
type EnumNumT CDev = CDev
instance Enum Natural where
type EnumNumT Natural = Natural
instance Enum DoTrace
instance Enum DoHeapProfile
instance Enum DoCostCentres
instance Enum GiveGCStats
instance (Integral a) => Enum (Ratio a) where
type EnumNumT (Ratio a) = Ratio a
type EnumIntegralT (Ratio a) = a
numStepsBetween x y stepSize = max (floor ((y - x) / stepSize + 1)) 0
instance (HasResolution a) => Enum (Fixed a) where
type EnumNumT (Fixed a) = Fixed a
type EnumIntegralT (Fixed a) = Integer
numStepsBetween x y stepSize = max (floor ((y - x) / stepSize + 1)) 0
instance Enum a => Enum (WrappedMonoid a) where
type EnumNumT (WrappedMonoid a) = EnumNumT a
type EnumIntegralT (WrappedMonoid a) = EnumIntegralT a
succ (WrapMonoid a) = WrapMonoid (succ a)
pred (WrapMonoid a) = WrapMonoid (pred a)
toEnum = WrapMonoid . toEnum
fromEnum = fromEnum . unwrapMonoid
numStepsBetween (WrapMonoid x) (WrapMonoid y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (Last a) where
type EnumNumT (Last a) = EnumNumT a
type EnumIntegralT (Last a) = EnumIntegralT a
succ (Last a) = Last (succ a)
pred (Last a) = Last (pred a)
toEnum = Last . toEnum
fromEnum = fromEnum . getLast
numStepsBetween (Last x) (Last y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (First a) where
type EnumNumT (First a) = EnumNumT a
type EnumIntegralT (First a) = EnumIntegralT a
succ (First a) = First (succ a)
pred (First a) = First (pred a)
toEnum = First . toEnum
fromEnum = fromEnum . getFirst
numStepsBetween (First x) (First y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (Max a) where
type EnumNumT (Max a) = EnumNumT a
type EnumIntegralT (Max a) = EnumIntegralT a
succ (Max a) = Max (succ a)
pred (Max a) = Max (pred a)
toEnum = Max . toEnum
fromEnum = fromEnum . getMax
numStepsBetween (Max x) (Max y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (Min a) where
type EnumNumT (Min a) = EnumNumT a
type EnumIntegralT (Min a) = EnumIntegralT a
succ (Min a) = Min (succ a)
pred (Min a) = Min (pred a)
toEnum = Min . toEnum
fromEnum = fromEnum . getMin
numStepsBetween (Min x) (Min y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (Identity a) where
type EnumNumT (Identity a) = EnumNumT a
type EnumIntegralT (Identity a) = EnumIntegralT a
succ (Identity a) = Identity (succ a)
pred (Identity a) = Identity (pred a)
toEnum = Identity . toEnum
fromEnum = fromEnum . runIdentity
numStepsBetween (Identity x) (Identity y) stepSize = numStepsBetween x y stepSize
instance Enum (Proxy s)
instance ((~) a b) => Enum ((:~:) a b)
instance (Coercible a b) => Enum (Coercion a b)
instance Enum (f a) => Enum (Alt f a) where
type EnumNumT (Alt f a) = EnumNumT (f a)
type EnumIntegralT (Alt f a) = EnumIntegralT (f a)
succ (Alt x) = Alt (succ x)
pred (Alt x) = Alt (pred x)
toEnum = Alt . toEnum
fromEnum = fromEnum . getAlt
numStepsBetween (Alt x) (Alt y) stepSize = numStepsBetween x y stepSize
instance Enum a => Enum (Const a b) where
type EnumNumT (Const a b) = EnumNumT a
type EnumIntegralT (Const a b) = EnumIntegralT a
succ (Const x) = Const (succ x)
pred (Const x) = Const (pred x)
toEnum = Const . toEnum
fromEnum = fromEnum . getConst
numStepsBetween (Const x) (Const y) stepSize = numStepsBetween x y stepSize
{-|
This specifies the type of elements of an instance of a class of either 'EnumFromTo' or 'EnumFrom'.
For example, the definition for lists is:
> type instance Element [a] = a
-}
type family Element a
{-|
The 'EnumFromTo' class defines versions of the Prelude 'Prelude.Enum' functions
'Prelude.enumFromTo' and 'Prelude.enumFromThenTo', as well as other functions which
may sometimes be more convienient.
But more importantly, it can produce any structure you define an instance for,
not just lists.
The only function that needs to be defined is 'enumFromStepCount',
default definitions will look after the rest.
Note that this class does not deal with the infinite list generating functions,
you'll need to look at the 'EnumFrom' class for that.
I've attempted to define appropriate instances for any structures in the core GHC
distribution, currently lists, arrays and bytestrings.
-}
class Enum (Element a) => EnumFromTo a where
{-| Much like 'Prelude.enumFromTo' from Prelude -}
enumFromTo :: Element a -> Element a -> a
enumFromTo x = enumFromStepTo x 1
{-| Much like 'Prelude.enumFromThenTo' from Prelude -}
enumFromThenTo :: Element a -> Element a -> Element a -> a
enumFromThenTo x next_x y = enumFromStepTo x (fromEnum next_x - fromEnum x) y
{-| This is like 'enumFromTo', but instead of a final stopping number, a count is given. -}
enumFromCount :: Element a -> EnumIntegralT (Element a) -> a
enumFromCount x = enumFromStepCount x 1
{-| This is like 'enumFromThenTo', but instead of a final stopping number, a count is given. -}
enumFromThenCount :: Element a -> Element a -> EnumIntegralT (Element a) -> a
enumFromThenCount x next_x = enumFromStepCount x (fromEnum next_x - fromEnum x)
{-| This is like 'enumFromThenTo', but instead of giving the second element directly, a step size is passed. -}
enumFromStepTo :: Element a -> EnumNumT (Element a) -> Element a -> a
enumFromStepTo x stepSize y = enumFromStepCount x stepSize (numStepsBetween x y stepSize)
{-|
This is a combination of the conviencience changes in 'enumFromThenCount' and 'enumFromStepTo'.
Instead of having to explicitly state the second element, a \"stepsize\" is passed,
Also, instead of stating the last element, a \"count\" is passed.
I find this tends to be more useful more often.
-}
enumFromStepCount :: Element a -> EnumNumT (Element a) -> EnumIntegralT (Element a) -> a
{-|
Much like the 'EnumFromTO' class, but defines the \"infinite\" Prelude Enum functions, namely
'Prelude.enumFrom' and 'Prelude.enumFromThen', as well as 'enumFromStep'.
The only function that needs to be defined is 'enumFromStep',
default definitions will look after the rest.
-}
class Enum (Element a) => EnumFrom a where
{-| Much like 'Prelude.enumFrom' from Prelude -}
enumFrom :: Element a -> a
enumFrom x = enumFromStep x 1
{-| Much like 'Prelude.enumFromThen' from Prelude -}
enumFromThen :: Element a -> Element a -> a
enumFromThen x next_x = enumFromStep x (fromEnum next_x - fromEnum x)
{-| Like 'enumFromThen', but with an explicit step size, not just the second element given. -}
enumFromStep :: Element a -> EnumNumT (Element a) -> a
default enumFromStep :: (Bounded (EnumIntegralT (Element a)), EnumFromTo a) => Element a -> EnumNumT (Element a) -> a
enumFromStep x stepSize = enumFromStepCount x stepSize maxBound
type instance Element [a] = a
instance Enum a => EnumFromTo [a] where
enumFromStepCount x stepSize count = go (fromEnum x) count where
go x n = case n of
0 -> []
_ -> (toEnum x):(go (x + stepSize) (n-1))
instance Enum a => EnumFrom [a] where
enumFromStep x stepSize = go (fromEnum x) where
go x = (toEnum x):(go (x + stepSize))
type instance Element (Array i e) = e
instance (Enum e, Ix i, Num i) => EnumFromTo (Array i e) where
enumFromStepCount x stepSize count = listArray (fromIntegral 0, fromIntegral (count - 1)) (enumFromStepCount x stepSize count)
type instance Element BS.ByteString = Word8
type instance Element BSL.ByteString = Word8
type instance Element BSS.ShortByteString = Word8
byteStringUnfoldrN f x stepSize count = let (r, _) = f (fromIntegral count) (\i -> Just (toEnum i, i + stepSize)) (fromEnum x) in r
instance EnumFromTo BS.ByteString where
enumFromStepCount = byteStringUnfoldrN BS.unfoldrN
instance EnumFromTo BSL.ByteString where
enumFromStepCount x stepSize count = BSL.unfoldr f (fromEnum x, count) where
f (i, n) = case n of
0 -> Nothing
_ -> Just (toEnum i, (i + stepSize, n - 1))
instance EnumFromTo BSS.ShortByteString where
enumFromStepCount x stepSize count = BSS.toShort (byteStringUnfoldrN BS.unfoldrN x stepSize count)