Finiteness.hs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. {-
  2. - Copyright (C) 2019 Koz Ross <koz.ross@retro-freedom.nz>
  3. -
  4. - This program is free software: you can redistribute it and/or modify
  5. - it under the terms of the GNU General Public License as published by
  6. - the Free Software Foundation, either version 3 of the License, or
  7. - (at your option) any later version.
  8. -
  9. - This program is distributed in the hope that it will be useful,
  10. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. - GNU General Public License for more details.
  13. -
  14. - You should have received a copy of the GNU General Public License
  15. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. -}
  17. {-# LANGUAGE TypeInType #-}
  18. {-# LANGUAGE DeriveDataTypeable #-}
  19. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  20. {-# LANGUAGE DeriveFunctor #-}
  21. {-# LANGUAGE Trustworthy #-}
  22. {-# LANGUAGE TypeOperators #-}
  23. {-# LANGUAGE TypeFamilies #-}
  24. {-# LANGUAGE TypeApplications #-}
  25. -- |
  26. -- Module: Data.Finitary.Finiteness
  27. -- Description: Newtype wrapper for deriving various typeclasses for
  28. -- @Finitary@ types.
  29. -- Copyright: (C) Koz Ross 2019
  30. -- License: GPL version 3.0 or later
  31. -- Maintainer: koz.ross@retro-freedom.nz
  32. -- Stability: Experimental
  33. -- Portability: GHC only
  34. --
  35. -- Knowing that a type @a@ is an instance of @Finitary@ gives us the knowledge
  36. -- that there is an isomorphism between @a@ and @Finite n@ for some @KnownNat
  37. -- n@. This gives us a lot of information, which we can exploit to automagically
  38. -- derive a range of type class instances.
  39. --
  40. -- 'Finiteness' is a @newtype@ wrapper providing this functionality, while
  41. -- 're-exporting' as many type class instances of the underlying type as
  42. -- possible. It is designed for use with @DerivingVia@ - an example of use:
  43. --
  44. -- > {-# LANGUAGE DerivingVia #-}
  45. -- > {-# LANGUAGE DeriveAnyClass #-}
  46. -- > {-# LANGUAGE DeriveGeneric #-}
  47. -- >
  48. -- > import GHC.Generics
  49. -- > import Data.Finitary
  50. -- > import Data.Finitary.Finiteness
  51. -- > import Data.Word
  52. -- > import Control.DeepSeq
  53. -- > import Data.Hashable
  54. -- > import Data.Binary
  55. -- >
  56. -- > data Foo = Bar | Baz (Word8, Word8) | Quux Word16
  57. -- > deriving (Eq, Generic, Finitary)
  58. -- > deriving (Ord, Bounded, NFData, Hashable, Binary) via (Finiteness Foo)
  59. --
  60. -- Currently, the following type class instances can be derived in this manner:
  61. --
  62. -- * 'Ord'
  63. -- * 'Bounded'
  64. -- * 'NFData'
  65. -- * 'Hashable'
  66. -- * 'Binary'
  67. --
  68. -- Additionally, 'Finiteness' \'forwards\' definitions of the following type
  69. -- classes:
  70. --
  71. -- * 'Eq'
  72. -- * 'Show'
  73. -- * 'Read'
  74. -- * 'Typeable'
  75. -- * 'Data'
  76. -- * 'Semigroup'
  77. -- * 'Monoid'
  78. module Data.Finitary.Finiteness
  79. (
  80. Finiteness(..)
  81. ) where
  82. import GHC.TypeNats
  83. import Data.Typeable (Typeable)
  84. import Data.Data (Data)
  85. import Data.Finitary (Finitary(..))
  86. import Data.Ord (comparing)
  87. import Control.DeepSeq (NFData(..))
  88. import Data.Hashable (Hashable(..))
  89. import Data.Binary (Binary(..))
  90. -- | Essentially 'Data.Functor.Identity' with a different name. Named this way due to the
  91. -- wordplay you get from use with @DerivingVia@.
  92. newtype Finiteness a = Finiteness { unFiniteness :: a }
  93. deriving (Eq, Show, Read, Typeable, Data, Functor, Semigroup, Monoid)
  94. -- | 'Finiteness' merely replicates the @Finitary@ behaviour of the underlying
  95. -- type.
  96. instance (Finitary a) => Finitary (Finiteness a) where
  97. type Cardinality (Finiteness a) = Cardinality a
  98. {-# INLINE fromFinite #-}
  99. fromFinite = Finiteness . fromFinite
  100. {-# INLINE toFinite #-}
  101. toFinite = toFinite . unFiniteness
  102. {-# INLINE start #-}
  103. start = Finiteness start
  104. {-# INLINE end #-}
  105. end = Finiteness end
  106. {-# INLINE previous #-}
  107. previous = fmap Finiteness . previous . unFiniteness
  108. {-# INLINE next #-}
  109. next = fmap Finiteness . next . unFiniteness
  110. -- | 'Ord' can be derived by deferring to the order on @Finite (Cardinality a)@.
  111. instance (Finitary a) => Ord (Finiteness a) where
  112. {-# INLINE compare #-}
  113. compare (Finiteness x) (Finiteness y) = comparing toFinite x y
  114. -- | Since any inhabited 'Finitary' type is also 'Bounded', we can forward this
  115. -- definition also.
  116. instance (Finitary a, 1 <= Cardinality a) => Bounded (Finiteness a) where
  117. {-# INLINE minBound #-}
  118. minBound = Finiteness start
  119. {-# INLINE maxBound #-}
  120. maxBound = Finiteness end
  121. -- | We can force evaluation of a 'Finitary' type by converting it to its index.
  122. instance (Finitary a) => NFData (Finiteness a) where
  123. {-# INLINE rnf #-}
  124. rnf = rnf . toFinite . unFiniteness
  125. -- | Any 'Finitary' type can be hashed by hashing its index.
  126. instance (Finitary a) => Hashable (Finiteness a) where
  127. {-# INLINE hashWithSalt #-}
  128. hashWithSalt salt = hashWithSalt salt . fromIntegral @_ @Integer . toFinite . unFiniteness
  129. -- | Any 'Finitary' type can be converted to a binary representation by
  130. -- converting its index.
  131. instance (Finitary a) => Binary (Finiteness a) where
  132. {-# INLINE put #-}
  133. put = put . fromIntegral @_ @Integer . toFinite . unFiniteness
  134. {-# INLINE get #-}
  135. get = Finiteness . fromFinite . fromIntegral @Integer <$> get