packages feed

generic-enum (empty) → 0.1.0.0

raw patch · 5 files changed

+642/−0 lines, 5 filesdep +arraydep +basedep +bytestringsetup-changed

Dependencies added: array, base, bytestring, hspec

Files

+ LICENSE view
@@ -0,0 +1,19 @@+Copyright 2017 Clinton Mead++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ generic-enum.cabal view
@@ -0,0 +1,43 @@+name:                 generic-enum+version:              0.1.0.0+synopsis:             An Enum class that fixes some deficiences with Prelude's Enum+description:+  The default Enum class has a couple of issues that limit it's flexibility namely:+  .+  1) The \"Enum\" type must be always \"Int\", even when this loses information, such as in+  the case of \"Integer\" (due to overflow) and \"Rational\" (in the case of fractions).+  .+  2) "enumFromTo" etc like functions can only produce lists, instead of directly producing other+  list like data types. Aside from effeciency, using a list as an intermediary can lose information also.+  .+  The Enum class this package defines should be largely a drop in replacement for Prelude's Enum,+  with some minor exceptions detailed in the documentation.+license: MIT+license-file: LICENSE+copyright: Clinton Mead (2017)+homepage:             +author:               Clinton Mead+maintainer:           clintonmead@gmail.com+category:             +build-type:           Simple+cabal-version:        >=1.10+tested-with: GHC == 8.0.2+bug-reports: https://github.com/clintonmead/generic-enum/issues++source-repository head+  type: git+  location: https://github.com/clintonmead/generic-enum.git++library+  exposed-modules: Data.Generic.Enum+  build-depends:        base == 4.9.*, array == 0.5.*, bytestring == 0.10.*+  hs-source-dirs:       src+  default-language:     Haskell2010++Test-Suite tests+  type: exitcode-stdio-1.0+  other-modules: Data.Generic.Enum+  main-is: Tests.hs+  build-depends:        base == 4.9.*, array == 0.5.*, bytestring == 0.10.*, hspec == 2.4.*+  hs-source-dirs:       src, test+  default-language:     Haskell2010
+ src/Data/Generic/Enum.hs view
@@ -0,0 +1,528 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}++module Data.Generic.Enum (+  Enum(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)+
+ test/Tests.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeApplications #-}+module Main where++import Prelude hiding (+  Enum, fromEnum, toEnum, succ, pred, enumFrom, enumFromThen, enumFromTo, enumFromThenTo+  )+import qualified Prelude as P+import Data.Generic.Enum++import Data.Generic.Enum (+  Element+  )++import Data.Proxy (Proxy(Proxy))+import Data.Ratio (Rational, (%))+import Data.Array (Array)+import Data.Word (Word8)++import qualified Data.ByteString as B++import Test.Hspec (hspec, it, shouldBe)++n1_int :: Int+n1_int = 2++n2_int :: Int+n2_int = 5++n1_rational :: Rational+n1_rational = 7 % 3++n2_rational :: Rational+n2_rational = 6 % 1++_a :: Word8+_a = P.toEnum (P.fromEnum 'a')++_e :: Word8+_e = P.toEnum (P.fromEnum 'e')++main = hspec $ do+  it "enumFromTo integer test" $ enumFromTo n1_int n2_int `shouldBe` P.enumFromTo n1_int n2_int+  it "enumFromTo rational test" $ sum ((enumFromTo n1_rational n2_rational) :: [Rational]) `shouldBe` (46 % 3)+  it "enumFromTo bytestring test" $ enumFromTo _a _e `shouldBe` B.pack [_a.._e]