clash-prelude
Copyright(C) 2025 Martijn Bastiaan
LicenseBSD2 (see the file LICENSE)
MaintainerQBayLogic B.V. <devops@qbaylogic.com>
Safe HaskellSafe-Inferred
LanguageHaskell2010

Clash.Class.NumConvert

Description

Utilities for converting between Clash number types in a non-erroring way. Its existence is motivated by the observation that Clash users often need to convert between different number types (e.g., Unsigned to Signed) and that it is not always clear how to do so properly. Two classes are exported:

  • NumConvert: for conversions that, based on types, are guaranteed to succeed.
  • MaybeNumConvert: for conversions that may fail for some values.

As opposed to fromIntegral, all conversions are translatable to synthesizable HDL.

Relation to convertible

Expand

Type classes exported here are similar to the convertible package in that it aims to facilitate conversions between different types. It is different in three ways:

  1. It offers no partial functions
  2. All its conversions are translatable to synthesizable HDL
  3. It is focused on (Clash's) number types

Implementing NumConvert for your own type

Expand

If you want to implement NumConvert and MaybeNumConvert for your own type, see NumConvertCanonical.

Synopsis

Documentation

type NumConvert a b = (NumConvertCanonical a (Canonical a), NumConvertCanonical (Canonical a) (Canonical b), NumConvertCanonical (Canonical b) b) Source #

Conversions that are, based on their types, guaranteed to succeed. A successful conversion retains the numerical value interpretation of the source type in the target type and does not produce errors.

Laws

Expand

A conversion is successful if a round trip conversion is lossless. I.e.,

Just x == maybeNumConvert (numConvert @a @b x)

for all values x of type a. It should also preserve the numerical value interpretation of the bits. For types that have an Integral instance, this intuition is captured by:

toInteger x == toInteger (numConvert @a @b x)

Instances should make sure their constraints are as "tight" as possible. I.e., if an instance's constraints cannot be satisfied, then for the same types maybeNumConvert should return Nothing for one or more values in the domain of the source type a:

L.any isNothing (L.map (maybeNumConvert @a @b) [minBound ..])

All implementations should be total, i.e., they should not produce "bottoms".

Additionally, any implementation should be translatable to synthesizable HDL.

numConvert :: forall a b. NumConvert a b => a -> b Source #

Convert a supplied value of type a to a value of type b. The conversion is guaranteed to succeed.

>>> numConvert (3 :: Index 8) :: Unsigned 8
3

The following will fail with a type error, as we cannot prove that all values of Index 8 can be represented by an Unsigned 2:

>>> numConvert (3 :: Index 8) :: Unsigned 2
...

For the time being, if the input is an XException, then the output is too. This property might be relaxed in the future.

type MaybeNumConvert a b = (NumConvertCanonical a (Canonical a), MaybeNumConvertCanonical (Canonical a) (Canonical b), NumConvertCanonical (Canonical b) b) Source #

Conversions that may fail for some values. A successful conversion retains the numerical value interpretation of the source type in the target type. A failure is expressed by returning Nothing, never by an XException.

Laws

Expand

A conversion is either successful or it fails gracefully. I.e., it does not produce errors (also see Clash.XException). I.e.,

x == fromMaybe x (maybeNumConvert @a @b x >>= maybeNumConvert @b @a)

for all values x of type a. It should also preserve the numerical value interpretation of the bits. For types that have an Integral instance, this intuition is captured by:

toInteger x == fromMaybe (toInteger x) (toInteger <$> maybeNumConvert @a @b x)

If a conversion succeeds one way, it should also succeed the other way. I.e.,

isJust (maybeNumConvert @a @b x) `implies` isJust (maybeNumConvert @a @b x >>= maybeNumConvert @b @a)

A conversion should succeed if and only if the value is representable in the target type. For types that have a Bounded and Integral instance, this intuition is captured by:

isJust (maybeNumConvert @a @b x) == (i x >= i (minBound @b) && i x <= i (maxBound @b))

where i = toInteger.

All implementations should be total, i.e., they should not produce "bottoms".

Additionally, any implementation should be translatable to synthesizable HDL.

maybeNumConvert :: forall a b. MaybeNumConvert a b => a -> Maybe b Source #

Convert a supplied value of type a to a value of type b. If the value cannot be represented in the target type, Nothing is returned.

>>> maybeNumConvert (1 :: Index 8) :: Maybe (Unsigned 2)
Just 1
>>> maybeNumConvert (7 :: Index 8) :: Maybe (Unsigned 2)
Nothing

For the time being, if the input is an XException, then the output is too. This property might be relaxed in the future.

For implementers only

class NumConvertCanonical a b where Source #

Internal class for concrete conversions. This class is used internally by NumConvert and should not be used directly. Use NumConvert instead.

If you want to provide an instance for your own type, please only make an instance for a direct mapping from and to Clash types and tell the instance what Clash type it corresponds to. For example, implementing Int16 looks like:

instance NumConvertCanonical Int16 (Signed 16) where
  numConvertCanonical = bitCoerce

instance NumConvertCanonical (Signed 16) Int16 where
  numConvertCanonical = bitCoerce

type instance Canonical Int16 = Signed 16

By doing this, your type will be convertable from and to any other type. For example:

>>> numConvert (10 :: Int16) :: Int32
10
>>> numConvert (15 :: Signed 8) :: Int16
15

Methods

numConvertCanonical :: a -> b Source #

Instances

Instances details
NumConvertCanonical Int16 (Signed 16) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Int32 (Signed 32) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Int64 (Signed 64) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Int8 (Signed 8) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Word16 (Unsigned 16) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Word32 (Unsigned 32) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Word64 (Unsigned 64) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Word8 (Unsigned 8) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Bit (BitVector 1) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Int (Signed 64) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical Word (Unsigned 64) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (BitVector 1) Bit Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Signed 8) Int8 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Signed 16) Int16 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Signed 32) Int32 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Signed 64) Int64 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Signed 64) Int Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Unsigned 8) Word8 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Unsigned 16) Word16 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Unsigned 32) Word32 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Unsigned 64) Word64 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

NumConvertCanonical (Unsigned 64) Word Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (BitVector n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, (2 ^ n) <= m) => NumConvertCanonical (BitVector n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, (n + 1) <= m) => NumConvertCanonical (BitVector n) (Signed m) Source #

Note: Conversion from BitVector 0 to Signed 0 is lossless, but not within the constraints of the instance.

Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (BitVector n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= (2 ^ m)) => NumConvertCanonical (Index n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (Index n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, (CLogWZ 2 n 0 + 1) <= m) => NumConvertCanonical (Index n) (Signed m) Source #

Note: Conversion from Index 1 to Signed 0 is lossless, but not within the constraints of the instance.

Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= (2 ^ m)) => NumConvertCanonical (Index n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (Signed n) (Signed m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (Unsigned n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, (2 ^ n) <= m) => NumConvertCanonical (Unsigned n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, (n + 1) <= m) => NumConvertCanonical (Unsigned n) (Signed m) Source #

Note: Conversion from Unsigned 0 to Signed 0 is lossless, but not within the constraints of the instance.

Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

(KnownNat n, KnownNat m, n <= m) => NumConvertCanonical (Unsigned n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.NumConvert

class MaybeNumConvertCanonical a b where Source #

Internal class for concrete conversions that may fail. This class is used internally by MaybeNumConvert and should not be used directly. Use MaybeNumConvert instead.

Instances

Instances details
(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (BitVector n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (BitVector n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (BitVector n) (Signed m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (BitVector n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Index n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Index n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Index n) (Signed m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Index n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Signed n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Signed n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Signed n) (Signed m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Signed n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Unsigned n) (BitVector m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Unsigned n) (Index m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Unsigned n) (Signed m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

(KnownNat n, KnownNat m) => MaybeNumConvertCanonical (Unsigned n) (Unsigned m) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.MaybeNumConvert

type family Canonical a Source #

Type family mapping types to their canonical "unwrapped" Clash form. This is used by numConvert and maybeNumConvert to determine the intermediate type.

Instances

Instances details
type Canonical Int16 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Int32 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Int64 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Int8 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Word16 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Word32 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Word64 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Word8 Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Bit Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Int Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical Int = Signed 64
type Canonical Word Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical (BitVector n) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical (Index n) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical (Index n) = Index n
type Canonical (Signed n) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical

type Canonical (Signed n) = Signed n
type Canonical (Unsigned n) Source # 
Instance details

Defined in Clash.Class.NumConvert.Internal.Canonical