Copyright | © 2014-2018 Herbert Valerio Riedel |
---|---|
License | BSD-style (see the LICENSE file) |
Maintainer | Herbert Valerio Riedel <hvr@gnu.org> |
Stability | experimental |
Portability | GHC ≥ 7.8 |
Safe Haskell | Safe |
Language | Haskell2010 |
This module provides for statically or dynamically checked
conversions between Integral
types.
Synopsis
- intCast :: (Integral a, Integral b, IsIntSubType a b ~ True) => a -> b
- intCastIso :: (Integral a, Integral b, IsIntTypeIso a b ~ True) => a -> b
- intCastEq :: (Integral a, Integral b, IsIntTypeEq a b ~ True) => a -> b
- intCastMaybe :: (Integral a, Integral b, Bits a, Bits b) => a -> Maybe b
- type family IntBaseType a :: IntBaseTypeK
- data IntBaseTypeK
- type IsIntSubType a b = IsIntBaseSubType (IntBaseType a) (IntBaseType b)
- type family IsIntBaseSubType a b :: Bool where ...
- type IsIntTypeIso a b = IsIntBaseTypeIso (IntBaseType a) (IntBaseType b)
- type family IsIntBaseTypeIso a b :: Bool where ...
- type IsIntTypeEq a b = IsIntBaseTypeEq (IntBaseType a) (IntBaseType b)
- type family IsIntBaseTypeEq (a :: IntBaseTypeK) (b :: IntBaseTypeK) :: Bool where ...
Conversion functions
statically checked
In the table below each cell denotes which of the three
intCast
, intCastIso
and intCastEq
conversion operations
are allowed (i.e. by the type-checker). The rows represent
the domain a
while the columns represent the codomain b
of the a->b
-typed conversion functions.
Natural | Word32 | Word64 | Int | |
Word | intCast | intCast & intCastEq & intCastIso | intCastIso | |
Word16 | intCast | intCast | intCast | intCast |
Int64 | intCastIso | intCast & intCastEq & intCastIso | ||
Int8 | intCast |
Note: The table above assumes a 64-bit platform (i.e. where finiteBitSize (0 :: Word) == 64
).
intCast :: (Integral a, Integral b, IsIntSubType a b ~ True) => a -> b Source #
Statically checked integer conversion which satisfies the property
Note: This is just a type-restricted alias of fromIntegral
and
should therefore lead to the same compiled code as if
fromIntegral
had been used instead of intCast
.
intCastIso :: (Integral a, Integral b, IsIntTypeIso a b ~ True) => a -> b Source #
Statically checked integer conversion which satisfies the properties
∀β .
intCastIso
(intCastIso
a ∷ β) == atoInteger
(intCastIso
a) ==toInteger
b (iftoInteger
a ==toInteger
b)
Note: This is just a type-restricted alias of fromIntegral
and
should therefore lead to the same compiled code as if
fromIntegral
had been used instead of intCastIso
.
intCastEq :: (Integral a, Integral b, IsIntTypeEq a b ~ True) => a -> b Source #
Version of intCast
restricted to casts between types with same value domain.
intCastEq
is the most constrained of the three conversions: The
existence of a intCastEq
conversion implies the existence of the
other two, i.e. intCastIso
and intCast
.
Note: This is just a type-restricted alias of fromIntegral
and
should therefore lead to the same compiled code as if
fromIntegral
had been used instead of intCastIso
.
dynamically checked
intCastMaybe :: (Integral a, Integral b, Bits a, Bits b) => a -> Maybe b Source #
Run-time-checked integer conversion
This is an optimized version of the following generic code below
intCastMaybeRef :: (Integral a, Integral b) => a -> Maybe b intCastMaybeRef x | toInteger x == toInteger y = Just y | otherwise = Nothing where y = fromIntegral x
The code above is rather inefficient as it needs to go via the
Integer
type. The function intCastMaybe
, however, is marked INLINEABLE
and
if both integral types are statically known, GHC will be able
optimize the code signficantly (for -O1
and better).
For instance (as of GHC 7.8.1) the following definitions
w16_to_i32 = intCastMaybe :: Word16 -> Maybe Int32 i16_to_w16 = intCastMaybe :: Int16 -> Maybe Word16
are translated into the following (simplified) GHC Core language
w16_to_i32 = \x -> Just (case x of _ { W16# x# -> I32# (word2Int# x#) }) i16_to_w16 = \x -> case eta of _ { I16# b1 -> case tagToEnum# (<=# 0 b1) of _ { False -> Nothing ; True -> Just (W16# (narrow16Word# (int2Word# b1))) } }
Note: Starting with base-4.8
, this function has been added to Data.Bits
under the name toIntegralSized
.
Registering new integer types
- For
intCastMaybe
you need to provide type-class instances ofBits
(andIntegral
). - For
intCast
,intCastIso
, andintCastEq
simply declare instances for theIntBaseType
type-family (as well as type-class instances ofIntegral
) as described below.
type family IntBaseType a :: IntBaseTypeK Source #
The (open) type family IntBaseType
encodes type-level
information about the value range of an integral type.
This module also provides type family instances for the standard
Haskell 2010 integral types (including Foreign.C.Types) as well
as the Natural
type.
Here's a simple example for registering a custom type with the Data.IntCast facilities:
-- user-implemented unsigned 4-bit integer data Nibble = … -- declare meta-information type instanceIntBaseType
Nibble =FixedWordTag
4 -- user-implemented signed 7-bit integer data MyInt7 = … -- declare meta-information type instanceIntBaseType
MyInt7 =FixedIntTag
7
The type-level predicate IsIntSubType
provides a partial
ordering based on the types above. See also intCast
.
Instances
data IntBaseTypeK Source #
(Kind) Meta-information about integral types.
If also a Bits
instance is defined, the type-level information
provided by IntBaseType
ought to match the meta-information that
is conveyed by the Bits
class' isSigned
and bitSizeMaybe
methods.
FixedIntTag Nat | fixed-width \(n\)-bit integers with value range \( \left[ -2^{n-1}, 2^{n-1}-1 \right] \). |
FixedWordTag Nat | fixed-width \(n\)-bit integers with value range \( \left[ 0, 2^{n} \right] \). |
BigIntTag | integers with value range \( \left] -\infty, +\infty \right[ \). |
BigWordTag | naturals with value range \( \left[ 0, +\infty \right[ \). |
Type-level predicates
The following type-level predicates are used by intCast
,
intCastIso
, and intCastEq
respectively.
type IsIntSubType a b = IsIntBaseSubType (IntBaseType a) (IntBaseType b) Source #
type family IsIntBaseSubType a b :: Bool where ... Source #
Closed type family providing the partial order of (improper) subtype-relations
IsIntSubType
provides a more convenient entry point.
IsIntBaseSubType a a = True | |
IsIntBaseSubType a BigIntTag = True | |
IsIntBaseSubType (FixedWordTag a) BigWordTag = True | |
IsIntBaseSubType (FixedIntTag a) (FixedIntTag b) = a <=? b | |
IsIntBaseSubType (FixedWordTag a) (FixedWordTag b) = a <=? b | |
IsIntBaseSubType (FixedWordTag a) (FixedIntTag b) = (a + 1) <=? b | |
IsIntBaseSubType a b = False |
type IsIntTypeIso a b = IsIntBaseTypeIso (IntBaseType a) (IntBaseType b) Source #
type family IsIntBaseTypeIso a b :: Bool where ... Source #
Closed type family representing an equality-relation on bit-width
This is a superset of the IsIntBaseTypeEq
relation, as it ignores
the signedness of fixed-size integers (i.e. Int32
is considered
equal to Word32
).
IsIntTypeIso
provides a more convenient entry point.
IsIntBaseTypeIso a a = True | |
IsIntBaseTypeIso (FixedIntTag n) (FixedWordTag n) = True | |
IsIntBaseTypeIso (FixedWordTag n) (FixedIntTag n) = True | |
IsIntBaseTypeIso a b = False |
type IsIntTypeEq a b = IsIntBaseTypeEq (IntBaseType a) (IntBaseType b) Source #
type family IsIntBaseTypeEq (a :: IntBaseTypeK) (b :: IntBaseTypeK) :: Bool where ... Source #
Closed type family representing an equality-relation on the integer base-type.
IsIntBaseTypeEq
provides a more convenient entry point.
IsIntBaseTypeEq a a = True | |
IsIntBaseTypeEq a b = False |