{-# LANGUAGE IncoherentInstances #-}
module Conversion
(
  Conversion(..), 
  convert1, 
  convert2,
) 
where

import BasePrelude


-- |
-- A type-class, 
-- which provides a non-partial conversion function from a value of type @a@ 
-- to a value of type @b@.
class Conversion a b where
  convert :: a -> b


-- |
-- Equivalent to 'id'.
instance Conversion a a where
  {-# INLINE convert #-}
  convert = id


-- |
-- Equivalent to 'atomically'.
instance Conversion (STM a) (IO a) where
  {-# INLINE convert #-}
  convert = atomically

-- |
-- Converts to any 'Alternative' type ('Maybe', list).
instance Alternative f => Conversion (Either a b) (f b) where
  {-# INLINE convert #-}
  convert = either (const empty) pure

-- |
-- Checks whether the value is 'Right'.
instance Conversion (Either a b) Bool where
  {-# INLINE convert #-}
  convert = either (const False) (const True)

-- |
-- Converts to any 'Alternative' type ('Either', list).
instance Alternative f => Conversion (Maybe a) (f a) where
  {-# INLINE convert #-}
  convert = maybe empty pure

-- |
-- Checks whether the value is 'Just'.
instance Conversion (Maybe a) Bool where
  {-# INLINE convert #-}
  convert = maybe False (const True)

-- |
-- Converts into a function, which extracts the value, 
-- given a default value in the 'Nothing' case.
-- 
-- Equivalent to 'fromMaybe'.
instance Conversion (Maybe a) (a -> a) where
  {-# INLINE convert #-}
  convert = flip fromMaybe

-- |
-- Gets the head of a list.
instance Alternative f => Conversion [a] (f a) where
  {-# INLINABLE convert #-}
  convert = \case [] -> empty; a : _ -> pure a

-- |
-- Checks whether the list is not empty.
instance Conversion [a] Bool where
  {-# INLINE convert #-}
  convert = null

-- |
-- Equivalent to 'catMaybes'.
instance Conversion [Maybe a] [a] where
  {-# INLINE convert #-}
  convert = catMaybes


instance Conversion Int Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Int32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Int Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Int8 Integer where
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int8 Int where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int8 Int16 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int8 Int32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int8 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int8 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int8 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int8 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int8 (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int8 (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Int16 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int16 Int where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int16 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Int16 Int32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int16 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int16 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int16 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int16 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int16 (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int16 (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Int32 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Int32 Int where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int32 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int32 (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Int32 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int32 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int32 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int32 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int32 (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int32 (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Int64 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Int64 (f Int) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Int32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Int64 (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Word Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word (f Int) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Int32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Int64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word Word64 where 
  {-# INLINE convert #-}
  convert = fromIntegral


instance Conversion Word8 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Int where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word8 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word8 Int16 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Int32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Word where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Word16 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Word32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word8 Word64 where 
  {-# INLINE convert #-}
  convert = fromIntegral


instance Conversion Word16 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word16 Int where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word16 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word16 (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word16 Int32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word16 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word16 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word16 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word16 Word32 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word16 Word64 where 
  {-# INLINE convert #-}
  convert = fromIntegral


instance Conversion Word32 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word32 (f Int) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word32 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word32 (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word32 (f Int32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word32 Int64 where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Conversion Word32 Word where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word32 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word32 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Conversion Word32 Word64 where 
  {-# INLINE convert #-}
  convert = fromIntegral


instance Conversion Word64 Integer where 
  {-# INLINE convert #-}
  convert = fromIntegral

instance Alternative f => Conversion Word64 (f Int) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Int8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Int16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Int32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Int64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Word64 (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Alternative f => Conversion Integer (f Int) where
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Int8) where
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Int16) where
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Int32) where
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Int64) where
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Word) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Word8) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Word16) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Word32) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral

instance Alternative f => Conversion Integer (f Word64) where 
  {-# INLINE convert #-}
  convert = checkedFromIntegral


instance Conversion Float Rational where
  {-# INLINE convert #-}
  convert = realToFrac

instance Conversion Float Double where
  {-# INLINE convert #-}
  convert = realToFrac


instance Conversion Double Rational where
  {-# INLINE convert #-}
  convert = realToFrac


{-# INLINABLE isomorphicallyChecked #-}
isomorphicallyChecked :: (Alternative f, Conversion b a, Eq a) => (a -> b) -> a -> f b
isomorphicallyChecked =
  \f a -> f a & \b -> if a == convert b then pure b else empty

{-# INLINABLE checkedFromIntegral #-}
checkedFromIntegral :: (Alternative f, Integral a, Integral b) => a -> f b
checkedFromIntegral =
  \a -> fromIntegral a & \b -> if fromIntegral b == a then pure b else empty

-- |
-- A utility, which helps the compiler resolve the type in case of conversion of types of kind @* -> *@.
{-# INLINE convert1 #-}
convert1 :: Conversion (a x) (b x) => a x -> b x
convert1 = 
  convert

-- |
-- A utility, which helps the compiler resolve the type in case of conversion of types of kind @* -> * -> *@.
{-# INLINE convert2 #-}
convert2 :: Conversion (a x1 x2) (b x1 x2) => a x1 x2 -> b x1 x2
convert2 = 
  convert
