Skip to content
Snippets Groups Projects
Converting.hs 2.57 KiB
Newer Older
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

{- | Conversion between types with a known level of safety.  *Heavily* inspired
 by `witch` (which has dependencies that make it hard for us to use just yet).
-}
module Tahoe.SDMF.Internal.Converting where

import Control.Monad.Fail (MonadFail)
import Data.Int (Int64)
import Data.Word (Word16, Word32, Word64, Word8)

-- | Precise, infallible conversion between two types.
class From a b where
    from :: a -> b

-- | Precise, fallible conversion between two types.
class TryFrom a b m where
    tryFrom ::
        -- | An error message for context if the conversion fails.
        String ->
        -- | The value to convert.
        a ->
        m b

instance MonadFail m => TryFrom Int Word32 m where
    tryFrom msg n
        | n < 0 = fail msg
        | n > maxWord32 = fail msg
        | otherwise = pure $ fromIntegral n
      where
        maxWord32 = from @Word32 @Int maxBound

instance MonadFail m => TryFrom Int Word64 m where
    tryFrom msg n
        | n < 0 = fail msg
        | otherwise = pure $ fromIntegral n

instance MonadFail m => TryFrom Int64 Word64 m where
    tryFrom msg n
        | n < 0 = fail msg
        | otherwise = pure $ fromIntegral n

instance From Word16 Int where
    from = fromIntegral

instance From Word8 Int where
    from = fromIntegral

instance From Word8 Word16 where
    from = fromIntegral

instance From Word32 Word64 where
    from = fromIntegral

instance From Word32 Int where
    from = fromIntegral

instance From Int64 Int where
    from = fromIntegral

instance From Int Int64 where
    from = fromIntegral

instance MonadFail m => TryFrom Word64 Int m where
    tryFrom msg n
        | n > maxInt = fail msg
        | otherwise = pure $ fromIntegral n
      where
        maxInt = fromIntegral (maxBound :: Int) :: Word64

instance MonadFail m => TryFrom Word16 Word8 m where
    tryFrom msg n
        | n > maxWord8 = fail msg
        | otherwise = pure $ fromIntegral n
      where
        maxWord8 = from @Word8 @Word16 maxBound

instance MonadFail m => TryFrom Word64 Int64 m where
    tryFrom msg n
        | n > maxInt64 = fail msg
        | otherwise = pure $ fromIntegral n
      where
        maxInt64 = fromIntegral (maxBound :: Int64) :: Word64

{- | Like `from` but with the order of the input/output type parameters
 reversed.
-}
into :: forall b a. From a b => a -> b
into = from

{- | Like `tryFrom` but with the order of the input/output type parameters
 reverse.
-}
tryInto :: forall b a m. TryFrom a b m => String -> a -> m b
tryInto = tryFrom