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-Inferred |
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
- = FixedIntTag Nat
- | FixedWordTag Nat
- | BigIntTag
- | BigWordTag
- 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 ∷ β) == a -
toInteger
(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 |