{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Internal workings of "AutoLift". You usually don't need to import
-- this module.
module AutoLift.Machinery (
AdHoc(..),
ShowDict(..), showDict, autoShow1, autoShow2,
ReadDict(..), readDict, autoRead1, autoRead2
) where
import Data.Reflection
import Data.Proxy
import Data.Coerce
import Text.Read
-- | Apply ad hoc instances on type @a@.
newtype AdHoc s a = AdHoc a
-- * Show
-- | Injected dictionary of Show
data ShowDict a = ShowDict
{ _showsPrec :: Int -> a -> ShowS
, _showList :: [a] -> ShowS
}
showDict :: forall a. Show a => ShowDict a
showDict = ShowDict { _showsPrec = showsPrec, _showList = showList }
{-# INLINE showDict #-}
instance (Reifies s (ShowDict a)) => Show (AdHoc s a) where
showsPrec = coerce $ _showsPrec (reflect (Proxy @s))
{-# INLINABLE showsPrec #-}
showList = coerce $ _showList (reflect (Proxy @s))
{-# INLINABLE showList #-}
{-
u/Iceland_jack taught me the technique to use QuantifiedConstraint on Coercible constraint. Thanks!
https://www.reddit.com/r/haskell_jp/comments/a75z0s/blog_reflection%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF/ed3efcv/
-}
-- | Automatic Show1
autoShow1 :: forall f b.
(forall a. Show a => Show (f a))
=> (forall x y. Coercible x y => Coercible (f x) (f y))
=> ShowDict b
-> ShowDict (f b)
autoShow1 showB = reify showB body
where
body :: forall name. Reifies name (ShowDict b) => Proxy name -> ShowDict (f b)
body _ = coerce $ showDict @(f (AdHoc name b))
{-# INLINABLE autoShow1 #-}
-- | Automatic Show2
autoShow2 :: forall f c d.
(forall a b. (Show a, Show b) => Show (f a b))
=> (forall x1 x2 y1 y2.
(Coercible x1 y1, Coercible x2 y2) => Coercible (f x1 x2) (f y1 y2)
)
=> ShowDict c
-> ShowDict d
-> ShowDict (f c d)
autoShow2 showC showD =
reify showC $ \proxyC ->
reify showD $ \proxyD ->
body proxyC proxyD
where
body :: forall name1 name2. (Reifies name1 (ShowDict c), Reifies name2 (ShowDict d))
=> Proxy name1 -> Proxy name2 -> ShowDict (f c d)
body _ _ = coerce $ showDict @(f (AdHoc name1 c) (AdHoc name2 d))
{-# INLINABLE autoShow2 #-}
-- * Read
-- | Injected dictionary of 'Read'
data ReadDict a = ReadDict
{ _readPrec :: ReadPrec a
, _readListPrec :: ReadPrec [a]
}
readDict :: forall a. Read a => ReadDict a
readDict = ReadDict{ _readPrec = readPrec, _readListPrec = readListPrec }
{-# INLINE readDict #-}
instance (Reifies s (ReadDict a)) => Read (AdHoc s a) where
readPrec = coerce $ _readPrec (reflect (Proxy @s))
{-# INLINABLE readPrec #-}
readListPrec = coerce $ _readListPrec (reflect (Proxy @s))
{-# INLINABLE readListPrec #-}
-- | Automatic Read1
autoRead1 :: forall f b.
(forall a. Read a => Read (f a))
=> (forall x y. Coercible x y => Coercible (f x) (f y))
=> ReadDict b
-> ReadDict (f b)
autoRead1 readB =
reify readB body
where
body :: forall name. (Reifies name (ReadDict b)) => Proxy name -> ReadDict (f b)
body _ = coerce (readDict @(f (AdHoc name b)))
{-# INLINABLE autoRead1 #-}
autoRead2 :: forall f c d.
(forall a b. (Read a, Read b) => Read (f a b))
=> (forall x1 x2 y1 y2.
(Coercible x1 y1, Coercible x2 y2) => Coercible (f x1 x2) (f y1 y2)
)
=> ReadDict c
-> ReadDict d
-> ReadDict (f c d)
autoRead2 readC readD =
reify readC $ \proxyC ->
reify readD $ \proxyD ->
body proxyC proxyD
where
body :: forall name1 name2. (Reifies name1 (ReadDict c), Reifies name2 (ReadDict d))
=> Proxy name1 -> Proxy name2 -> ReadDict (f c d)
body _ _ = coerce (readDict @(f (AdHoc name1 c) (AdHoc name2 d)))
{-# INLINABLE autoRead2 #-}