finite-table-0.1.0.0: src/Data/Finite.hs
-- Copyright 2019-2021 Google LLC
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoStarIsType #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Provides a class of types isomorphic to some statically-known @'Fin' n@.
--
-- This comes with Generics-based generated instances, and can be used to
-- generate instances of 'Enum' and 'Bounded' (for which the stock deriving
-- only supports sum types with no fields).
--
-- Since this is all still represented by 'Int' internally, things will start
-- raising 'error's if your type has more values than can fit in positive
-- 'Int's. It's not recommended to use this on large types, and there's not
-- much reason to want to anyway, as its main uses are to derive 'Enum' (which
-- is also based on 'Int') and to make the type compatible with
-- 'Data.Finite.Table.Table' (which would be impractically large for a key type
-- with too many values to represent as 'Int').
--
-- The most common way to get a 'Finite' instance for a type is to tack on a
-- @deriving Finite via 'Wrapped' 'Generic' MyType@ clause, which results in an
-- automatically-generated instance based on the type's ADT structure.
--
-- This also provides instances @'Enum' (Wrapped Finite a)@ and
-- @'Bounded' (Wrapped Finite a)@, so some types that would otherwise not be
-- compatible with derived 'Enum' instances can get them by adding a
-- @deriving (Enum, Bounded) via Wrapped Finite MyType@ clause.
module Data.Finite
( -- * Finite Enumerations
Finite(..), cardinality, enumerate, asFin
-- * Implementation Details
, SC, GFinite(..), GCardinality
) where
import Data.Functor.Identity (Identity)
import Data.Int (Int8, Int16)
import Data.Proxy (Proxy(..))
import Data.Semigroup (WrappedMonoid, Min, Max, First, Last)
import Data.Void (Void)
import Data.Word (Word8, Word16)
import GHC.Generics
( Generic(..), V1, U1(..), M1(..), K1(..), (:+:)(..), (:*:)(..)
)
import GHC.TypeNats (type (+), type (*), type (<=), KnownNat, Nat, natVal)
import Control.Lens (Iso', iso)
import Data.SInt (SInt, sintVal, addSInt, mulSInt, staticSIntVal, reifySInt)
import Data.Fin.Int.Explicit
( enumFin, concatFin, splitFin, crossFin, divModFin, minFin, maxFin
, fin
)
import Data.Fin.Int (Fin, finToInt, unsafeFin)
import qualified Data.Vec.Short as V
import Data.Wrapped (Wrapped(..))
-- | A typeclass of finite enumerable types.
--
-- These allow constructing 'Data.Functor.Rep.Representable' Functors using a
-- simple 'Data.Vec.Short.Vec' as the underlying storage, with constant-time
-- lookup and efficient traversals.
--
-- Note that since 'Fin' is (currently) represented by 'Int', any type with
-- more values than 'Int' can't have an instance. This means we can't have
-- instances for 32- and 64-bit arithmetic types, since 'Int' is only required
-- to have 30 bits of precision.
--
-- Annoyingly, we also can't have an instance for 'Int' and 'Word', because
-- 'Fin' wastes one bit of the 'Int' by forbidding negative values. The
-- cardinality of 'Int' and 'Word' would need to be twice as large as we can
-- actually represent in a 'Fin'. Another obstacle is that their cardinality
-- varies between implementations and architectures; it's possible to work
-- around this by making their Cardinality an irreducible type family
-- application, and using 'Data.SInt.SI#' to plug in a value at runtime, but
-- this makes the 'Fin's related to 'Int' and 'Word' annoying to work with,
-- since their bound is only known at runtime.
--
-- Fortunately, those instances are unlikely to be important, since a table of
-- 2^32 elements is moderately impractical (32GiB of pointers alone), and a
-- table of 2^64 elements is unrepresentable in current computer architectures.
--
-- 'toFin' and 'fromFin' shall be total functions and shall be the two sides of
-- an isomorphism.
class Finite a where
type Cardinality a :: Nat
-- | A witness that the cardinality is known at runtime.
--
-- This isn't part of the class context because we can only perform
-- arithmetic on 'KnownNat' instances in expression context; that is, we
-- can't convince GHC that an instance with
-- @type Cardinality (Maybe a) = Cardinality a + 1@ is valid if the
-- 'KnownNat' is in the class context. Instead, we use 'SInt' to allow
-- computing the cardinality at runtime.
cardinality' :: SC a (Cardinality a)
toFin :: a -> Fin (Cardinality a)
fromFin :: Fin (Cardinality a) -> a
-- | A wrapper type around @'Cardinality' a@ to support DerivingVia on GHC 8.6.
--
-- Instance methods that don't mention the instance head outside of type
-- families / aliases don't work with DerivingVia on GHC 8.6 because it uses
-- type signatures rather than TypeApplications to choose the instance to call
-- into.
newtype SC a n = SC { getSC :: SInt n }
-- | A witness that the cardinality of @a@ is known at runtime.
cardinality :: forall a. Finite a => SInt (Cardinality a)
cardinality = getSC (cardinality' @a)
-- | Generate a list containing every value of @a@.
enumerate :: forall a. Finite a => [a]
enumerate = fromFin <$> enumFin (cardinality @a)
-- | Implement 'toFin' by 'fromEnum'.
--
-- This should only be used for types with 'fromEnum' range @0..Cardinality a@;
-- this is notably not the case for signed integer types, which have negative
-- 'fromEnum' values.
toFinEnum :: Enum a => SInt (Cardinality a) -> a -> Fin (Cardinality a)
toFinEnum sn = fin sn . fromEnum
-- | Implement 'fromFin' by 'toEnum'.
--
-- The same restrictions apply as for 'toFinEnum'.
fromFinEnum :: Enum a => Fin (Cardinality a) -> a
fromFinEnum = toEnum . finToInt
instance Finite Char where
type Cardinality Char = 1114112 -- According to 'minBound' and 'maxBound'
cardinality' = SC staticSIntVal
toFin = toFinEnum staticSIntVal
fromFin = fromFinEnum
toFinExcessK
:: forall n a. (KnownNat n, Integral a) => a -> Fin (Cardinality a)
toFinExcessK =
unsafeFin . (+ (fromIntegral (natVal @n Proxy) :: Int)) . fromIntegral
fromFinExcessK
:: forall n a. (KnownNat n, Integral a) => Fin (Cardinality a) -> a
fromFinExcessK =
subtract (fromIntegral (natVal @n Proxy)) . fromIntegral . finToInt
instance Finite Int8 where
type Cardinality Int8 = 256
cardinality' = SC staticSIntVal
toFin = toFinExcessK @128
fromFin = fromFinExcessK @128
instance Finite Int16 where
type Cardinality Int16 = 65536
cardinality' = SC staticSIntVal
toFin = toFinExcessK @32768
fromFin = fromFinExcessK @32768
instance Finite Word8 where
type Cardinality Word8 = 256
cardinality' = SC staticSIntVal
toFin = unsafeFin . id @Int . fromIntegral
fromFin = fromIntegral . finToInt
instance Finite Word16 where
type Cardinality Word16 = 65536
cardinality' = SC staticSIntVal
toFin = unsafeFin . id @Int . fromIntegral
fromFin = fromIntegral . finToInt
instance KnownNat n => Finite (Fin n) where
type Cardinality (Fin n) = n
cardinality' = SC sintVal
toFin = id
fromFin = id
-- Aesthetics: make more derived instances fit on one line.
type G = Wrapped Generic
deriving via G () instance Finite ()
deriving via G Bool instance Finite Bool
deriving via G Ordering instance Finite Ordering
deriving via G Void instance Finite Void
deriving via G (Identity a) instance Finite a => Finite (Identity a)
deriving via G (WrappedMonoid a) instance Finite a => Finite (WrappedMonoid a)
deriving via G (Last a) instance Finite a => Finite (Last a)
deriving via G (First a) instance Finite a => Finite (First a)
deriving via G (Max a) instance Finite a => Finite (Max a)
deriving via G (Min a) instance Finite a => Finite (Min a)
deriving via G (Maybe a) instance Finite a => Finite (Maybe a)
deriving via G (Either a b) instance (Finite a, Finite b) => Finite (Either a b)
deriving via G (a, b) instance (Finite a, Finite b) => Finite (a, b)
deriving via G (a, b, c)
instance (Finite a, Finite b, Finite c) => Finite (a, b, c)
deriving via G (a, b, c, d)
instance (Finite a, Finite b, Finite c, Finite d) => Finite (a, b, c, d)
deriving via G (a, b, c, d, e)
instance (Finite a, Finite b, Finite c, Finite d, Finite e)
=> Finite (a, b, c, d, e)
instance (Generic a, GFinite (Rep a)) => Finite (Wrapped Generic a) where
type Cardinality (Wrapped Generic a) = GCardinality (Rep a)
cardinality' = SC $ gcardinality @(Rep a)
toFin = gtoFin . from . unWrapped
fromFin = Wrapped . to . gfromFin
-- | The derived cardinality of a generic representation type.
type family GCardinality a where
GCardinality V1 = 0
GCardinality U1 = 1
GCardinality (K1 i a) = Cardinality a
GCardinality (M1 i c f) = GCardinality f
GCardinality (f :+: g) = GCardinality f + GCardinality g
GCardinality (f :*: g) = GCardinality f * GCardinality g
-- | The derived 'Finite' implementation of a generic representation type.
class GFinite a where
gcardinality :: SInt (GCardinality a)
gtoFin :: a p -> Fin (GCardinality a)
gfromFin :: Fin (GCardinality a) -> a p
instance GFinite V1 where
gcardinality = staticSIntVal
gtoFin x = case x of {}
gfromFin x = V.nil V.! x
instance GFinite U1 where
gcardinality = staticSIntVal
gtoFin U1 = minFin
gfromFin !_ = U1
instance Finite a => GFinite (K1 i a) where
gcardinality = cardinality @a
gtoFin = toFin . unK1
gfromFin = K1 . fromFin
instance GFinite f => GFinite (M1 i c f) where
gcardinality = gcardinality @f
gtoFin = gtoFin . unM1
gfromFin = M1 . gfromFin
instance (GFinite f, GFinite g) => GFinite (f :+: g) where
gcardinality = gcardinality @f `addSInt` gcardinality @g
gtoFin x = concatFin (gcardinality @f) $ case x of
L1 f -> Left $ gtoFin f
R1 g -> Right $ gtoFin g
gfromFin =
either (L1 . gfromFin) (R1 . gfromFin) . splitFin (gcardinality @f)
{-# INLINE gtoFin #-}
{-# INLINE gfromFin #-}
instance (GFinite f, GFinite g) => GFinite (f :*: g) where
gcardinality = gcardinality @f `mulSInt` gcardinality @g
gtoFin (f :*: g) = crossFin (gcardinality @g) (gtoFin f) (gtoFin g)
gfromFin x =
let (f, g) = divModFin (gcardinality @g) x
in gfromFin f :*: gfromFin g
{-# INLINE gtoFin #-}
{-# INLINE gfromFin #-}
-- | An 'Control.Lens.Iso' between @a@ and the corresponding 'Fin' type.
asFin :: Finite a => Iso' a (Fin (Cardinality a))
asFin = iso toFin fromFin
instance Finite a => Enum (Wrapped Finite a) where
toEnum = Wrapped . fromFin . fin (cardinality @a)
fromEnum = finToInt . toFin . unWrapped
enumFrom = reifySInt (cardinality @a) $
fmap (Wrapped . fromFin) . enumFrom . toFin . unWrapped
enumFromThen (Wrapped x) = reifySInt (cardinality @a) $
fmap (Wrapped . fromFin) . enumFromThen (toFin x) . toFin . unWrapped
enumFromTo (Wrapped x) = reifySInt (cardinality @a) $
fmap (Wrapped . fromFin) . enumFromTo (toFin x) . toFin . unWrapped
enumFromThenTo (Wrapped x) (Wrapped y) = reifySInt (cardinality @a) $
fmap (Wrapped . fromFin) . enumFromThenTo (toFin x) (toFin y) .
toFin . unWrapped
instance (Finite a, 1 <= Cardinality a) => Bounded (Wrapped Finite a) where
minBound = Wrapped $ fromFin minFin
maxBound = Wrapped $ fromFin (maxFin (cardinality @a))