{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE UndecidableSuperClasses #-}
module Control.Isomorphic where
import Control.Applicative
import Control.Arrow
import qualified Control.Monad.ST.Lazy as SL
import qualified Control.Monad.ST.Strict as SS
import qualified Control.Newtype.Generics as N
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BLC
import Data.Coerce
import Data.Fixed
import Data.Functor.Compose
import Data.Functor.Identity
import qualified Data.List.NonEmpty as NE
import Data.Monoid
import Data.Profunctor (Profunctor (..))
import qualified Data.Text as TS
import qualified Data.Text.Encoding as TSE
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TLE
import GHC.Generics (C1, D1, Generic, K1 (..), M1 (..),
S1)
class Isomorphic b a => Isomorphic a b where
to' :: a -> b
instance Isomorphic (D1 d (C1 c (S1 s (K1 i a))) a) a where
to' (M1 (M1 (M1 (K1 x)))) = x
instance Isomorphic a (D1 d (C1 c (S1 s (K1 i a))) a) where
to' x = (M1 (M1 (M1 (K1 x))))
via :: forall b a c. (Isomorphic b c, Isomorphic a b) => a -> c
via = from @b . to
{-# INLINE via #-}
to :: forall b a. Isomorphic a b => a -> b
to = to'
{-# INLINE to #-}
from :: forall a b. Isomorphic a b => a -> b
from = to'
{-# INLINE from #-}
instance {-# OVERLAPPABLE #-} Isomorphic a a where to' = id
{-# INLINE to' #-}
instance Isomorphic (a,b) (b,a) where to' (a,b) = (b,a)
{-# INLINABLE to' #-}
instance Isomorphic (a,(b,c)) ((a,b),c) where to' (a,(b,c)) = ((a,b),c)
{-# INLINABLE to' #-}
instance Isomorphic ((a,b),c) (a,(b,c)) where to' ((a,b),c) = (a,(b,c))
{-# INLINABLE to' #-}
instance Isomorphic (a,b,c) (a,(b,c)) where to' (a,b,c) = (a,(b,c))
{-# INLINABLE to' #-}
instance Isomorphic (a,(b,c)) (a,b,c) where to' (a,(b,c)) = (a,b,c)
{-# INLINABLE to' #-}
instance Isomorphic (a,b,c) ((a,b),c) where to' (a,b,c) = ((a,b),c)
{-# INLINABLE to' #-}
instance Isomorphic ((a,b),c) (a,b,c) where to' ((a,b),c) = (a,b,c)
{-# INLINABLE to' #-}
instance Isomorphic BS.ByteString String where
to' = BSC.unpack
{-# INLINE to' #-}
instance Isomorphic String BS.ByteString where
to' = BSC.pack
{-# INLINE to' #-}
instance Isomorphic BL.ByteString String where
to' = BLC.unpack
{-# INLINE to' #-}
instance Isomorphic String BL.ByteString where
to' = BLC.pack
{-# INLINE to' #-}
instance Isomorphic TS.Text String where
to' = TS.unpack
{-# INLINE to' #-}
instance Isomorphic String TS.Text where
to' = TS.pack
{-# INLINE to' #-}
instance Isomorphic TL.Text String where
to' = TL.unpack
{-# INLINE to' #-}
instance Isomorphic String TL.Text where
to' = TL.pack
{-# INLINE to' #-}
instance Isomorphic TS.Text BS.ByteString where
to' = TSE.encodeUtf8
{-# INLINE to' #-}
instance Isomorphic BS.ByteString TS.Text where
to' = TSE.decodeUtf8
{-# INLINE to' #-}
instance Isomorphic TS.Text BL.ByteString where
to' = BL.fromStrict . to
{-# INLINE to' #-}
instance Isomorphic BL.ByteString TS.Text where
to' = to' . BL.toStrict
{-# INLINE to' #-}
instance Isomorphic TS.Text TL.Text where
to' = TL.fromStrict
{-# INLINE to' #-}
instance Isomorphic TL.Text TS.Text where
to' = TL.toStrict
{-# INLINE to' #-}
instance Isomorphic TL.Text BS.ByteString where
to' = BL.toStrict . to
{-# INLINE to' #-}
instance Isomorphic BS.ByteString TL.Text where
to' = to' . BL.fromStrict
{-# INLINE to' #-}
instance Isomorphic TL.Text BL.ByteString where
to' = TLE.encodeUtf8
{-# INLINE to' #-}
instance Isomorphic BL.ByteString TL.Text where
to' = TLE.decodeUtf8
{-# INLINE to' #-}
instance Isomorphic BS.ByteString BL.ByteString where
to' = BL.fromStrict
{-# INLINE to' #-}
instance Isomorphic BL.ByteString BS.ByteString where
to' = BL.toStrict
{-# INLINE to' #-}
instance Isomorphic (Maybe a) (Either () a) where
to' = \case Just a -> Right a; _ -> Left ()
instance Isomorphic (Either () a) (Maybe a) where
to' = \case Right a -> Just a; _ -> Nothing
instance Isomorphic (Maybe a) (Either a ()) where
to' = \case Just a -> Left a; _ -> Right ()
instance Isomorphic (Either a ()) (Maybe a) where
to' = \case Left a -> Just a; _ -> Nothing
instance Isomorphic (a -> b -> c) ((a,b) -> c) where to' = uncurry
{-# INLINE to' #-}
instance Isomorphic ((a,b) -> c) (a -> b -> c) where to' = curry
{-# INLINE to' #-}
instance Isomorphic (a -> b -> c) (b -> a -> c) where to' = flip
{-# INLINE to' #-}
instance Isomorphic (Either a b) (Either b a) where
to' = \case Right x -> Left x; Left x -> Right x
instance Isomorphic (NE.NonEmpty a) (a, [a]) where to' (x NE.:| xs) = (x,xs)
instance Isomorphic (a, [a]) (NE.NonEmpty a) where to' (x,xs) = x NE.:| xs
newtype IsRight = IsRight { unIsRight :: Either () () } deriving (Show, Generic)
newtype IsLeft = IsLeft { unIsLeft :: Either () () } deriving (Show, Generic)
instance Isomorphic IsRight (Either () ()) where to' = unIsRight
instance Isomorphic (Either () ()) IsRight where to' = IsRight
instance Isomorphic IsLeft (Either () ()) where to' = unIsLeft
instance Isomorphic (Either () ()) IsLeft where to' = IsLeft
instance Isomorphic Bool IsRight where
to' True = IsRight (Right ())
to' _ = IsRight (Left ())
instance Isomorphic IsRight Bool where
to' (IsRight (Right ())) = True
to' _ = False
instance Isomorphic Bool IsLeft where
to' True = IsLeft (Left ())
to' _ = IsLeft (Right ())
instance Isomorphic IsLeft Bool where
to' (IsLeft (Left ())) = True
to' _ = False
instance Enum a => Isomorphic a Int where to' = fromEnum
{-# INLINE to' #-}
instance Enum a => Isomorphic Int a where to' = toEnum
{-# INLINE to' #-}
instance Isomorphic (SL.ST s a) (SS.ST s a) where to' = SL.lazyToStrictST
{-# INLINE to' #-}
instance Isomorphic (SS.ST s a) (SL.ST s a) where to' = SL.strictToLazyST
{-# INLINE to' #-}
newtype IsJust = IsJust { unIsJust :: Maybe () } deriving Show
newtype IsNothing = IsNothing { unIsNothing :: Maybe () } deriving Show
instance Isomorphic IsJust (Maybe ()) where to' = unIsJust
instance Isomorphic (Maybe ()) IsJust where to' = IsJust
instance Isomorphic IsNothing (Maybe ()) where to' = unIsNothing
instance Isomorphic (Maybe ()) IsNothing where to' = IsNothing
instance Isomorphic Bool IsJust where
to' True = IsJust (Just ())
to' _ = IsJust Nothing
instance Isomorphic IsJust Bool where
to' (IsJust (Just ())) = True
to' _ = False
instance Isomorphic Bool IsNothing where
to' True = IsNothing Nothing
to' _ = IsNothing (Just ())
instance Isomorphic IsNothing Bool where
to' (IsNothing Nothing) = True
to' _ = False
instance Isomorphic (WrappedMonad m a) (m a) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (m a) (WrappedMonad m a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (WrappedArrow a b c) (a b c) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (a b c) (WrappedArrow a b c) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (ZipList a) [a] where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic [a] (ZipList a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Kleisli m a b) (a -> m b) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (a -> m b) (Kleisli m a b) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (ArrowMonad a b) (a () b) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (a () b) (ArrowMonad a b) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Fixed a) Integer where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic Integer (Fixed a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Compose f g a) (f (g a)) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (f (g a)) (Compose f g a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Const a x) a where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic a (Const a x) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Identity a) a where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic a (Identity a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Dual a) a where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic a (Dual a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Endo a) (a -> a) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (a -> a) (Endo a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic All Bool where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic Bool All where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic Any Bool where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic Bool Any where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Sum a) a where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic a (Sum a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Product a) a where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic a (Product a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (First a) (Maybe a) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (Maybe a) (First a) where to' = N.pack
{-# INLINE to' #-}
instance Isomorphic (Last a) (Maybe a) where to' = N.unpack
{-# INLINE to' #-}
instance Isomorphic (Maybe a) (Last a) where to' = N.pack
{-# INLINE to' #-}
as :: (Isomorphic b d, Isomorphic c a) => (a -> b) -> c -> d
as f = to . f . to
as2 :: (Isomorphic c f, Isomorphic d a, Isomorphic e b) => (a -> b -> c) -> d -> e -> f
as2 f x y = to $ f (to x) (to y)
as3 :: (Isomorphic d h, Isomorphic e a, Isomorphic f b, Isomorphic g c) => (a -> b -> c -> d) -> e -> f -> g -> h
as3 f x y z = to $ f (to x) (to y) (to z)
as4 :: (Isomorphic e j, Isomorphic f a, Isomorphic g b, Isomorphic h c, Isomorphic i d) => (a -> b -> c -> d -> e) -> f -> g -> h -> i -> j
as4 f w x y z = to $ f (to w) (to x) (to y) (to z)
as5 :: (Isomorphic f l, Isomorphic g a, Isomorphic h b, Isomorphic i c, Isomorphic j d, Isomorphic k e) => (a -> b -> c -> d -> e -> f) -> g -> h -> i -> j -> k -> l
as5 f v w x y z = to $ f (to v) (to w) (to x) (to y) (to z)
isoBi :: (Profunctor p, Isomorphic s a, Isomorphic b t, Functor f) => p a (f b) -> p s (f t)
isoBi = dimap to (fmap to)
instance {-# OVERLAPPABLE #-} Coercible a b => Isomorphic a b where to' = coerce
{-# INLINE to' #-}
instance (Functor f, Isomorphic a b) => Isomorphic (f a) (f b) where to' = fmap to