Skip to content

Instantly share code, notes, and snippets.

@mizunashi-mana
Created December 21, 2018 07:22
Show Gist options
  • Select an option

  • Save mizunashi-mana/76f15ec8b985957f49ea37c4645b6572 to your computer and use it in GitHub Desktop.

Select an option

Save mizunashi-mana/76f15ec8b985957f49ea37c4645b6572 to your computer and use it in GitHub Desktop.
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Functor.Classes.AutoDerive
( DerivingShow1 (..)
, DerivingShow2 (..)
, ReprConstraint (..)
, ReprDict (..)
, DictReceiver (..)
, innerDictReceiveCoerce
, Bicontravariant (..)
, Joker (..)
) where
import Data.Bifunctor
import Data.Coerce
import Data.Constraint
import Data.Functor.Classes
import Data.Reflection
import Unsafe.Coerce
class ReprConstraint c where
data ReprDict c
default fromReprDict :: (Given (ReprDict c) => c) => ReprDict c -> Dict c
fromReprDict = reifyGift (Gift Dict)
fromReprDict :: ReprDict c -> Dict c
newtype DictReceiver a = DictReceiver a
type ReceiveReprDict c a = ReprDict (c (DictReceiver a))
innerDictReceiveCoerce ::
( Bicontravariant f
, Coercible (f (DictReceiver a) (DictReceiver b)) fdab, Coercible (f a b) fab
) => (fdab -> f (DictReceiver a) (DictReceiver b)) -> (f a b -> fab) -> fdab -> fab
innerDictReceiveCoerce = innerBicontramapCoerce
{-# INLINE innerDictReceiveCoerce #-}
instance ReprConstraint (Eq (DictReceiver a)) where
newtype ReprDict (Eq (DictReceiver a)) = EqDict
{ eqDict_equal :: a -> a -> Bool
}
instance Given (ReceiveReprDict Eq a) => Eq (DictReceiver a) where
(==) = coerce $ eqDict_equal (given @(ReceiveReprDict Eq a))
instance ReprConstraint (Show (DictReceiver a)) where
data ReprDict (Show (DictReceiver a)) = ShowDict
{ showDict_showsPrec :: Int -> a -> ShowS
, showDict_showList :: [a] -> ShowS
}
instance Given (ReceiveReprDict Show a) => Show (DictReceiver a) where
showsPrec = coerce $ showDict_showsPrec (given @(ReceiveReprDict Show a))
showList = coerce $ showDict_showList (given @(ReceiveReprDict Show a))
newtype BiliftedMethod_showsPrec f a b = BiliftedMethod_showsPrec
{ getBiliftedMethod_showsPrec :: Int -> f a b -> ShowS
}
liftedMethod_showsPrec :: (Int -> f a -> ShowS) -> BiliftedMethod_showsPrec (Joker f) b a
liftedMethod_showsPrec = coerce
getLiftedMethod_showsPrec :: BiliftedMethod_showsPrec (Joker f) b a -> (Int -> f a -> ShowS)
getLiftedMethod_showsPrec = coerce
instance Bifunctor f => Bicontravariant (BiliftedMethod_showsPrec f) where
bicontramap f g (BiliftedMethod_showsPrec m) = BiliftedMethod_showsPrec \p -> m p . bimap f g
newtype BiliftedMethod_showList f a b = BiliftedMethod_showList
{ getBiliftedMethod_showList :: [f a b] -> ShowS
}
liftedMethod_showList :: ([f a] -> ShowS) -> BiliftedMethod_showList (Joker f) b a
liftedMethod_showList = coerce
getLiftedMethod_showList :: BiliftedMethod_showList (Joker f) b a -> ([f a] -> ShowS)
getLiftedMethod_showList = coerce
instance Bifunctor f => Bicontravariant (BiliftedMethod_showList f) where
bicontramap f g (BiliftedMethod_showList m) = BiliftedMethod_showList $ m . fmap (bimap f g)
-- | An instance template of @Show1@ for @DerivingVia@
--
-- Example:
--
-- >>> :set -XDerivingVia -XDeriveFunctor -XStandaloneDeriving
-- >>> import Data.Functor.Classes
-- >>> data F a = F a deriving (Show, Functor)
-- >>> deriving via (DerivingShow1 F) instance Show1 F
-- >>> showsPrec1 0 (F (F True)) ""
-- "F (F True)"
-- >>> showsPrec 0 (F (F True)) ""
-- "F (F True)"
--
newtype DerivingShow1 f a = DerivingShow1 (f a)
deriving Functor
instance Show (f a) => Show (DerivingShow1 f a) where
showsPrec = coerce $ showsPrec @(f a)
showList = coerce $ showList @(f a)
show = coerce $ show @(f a)
instance (Functor f, forall x. Show x => Show (f x)) => Show1 (DerivingShow1 f) where
liftShowsPrec _showsPrec _showList =
case fromReprDict $ ShowDict _showsPrec _showList of
Dict -> innerDictReceiveCoerce liftedMethod_showsPrec getLiftedMethod_showsPrec showsPrec
liftShowList _showsPrec _showList =
case fromReprDict $ ShowDict _showsPrec _showList of
Dict -> innerDictReceiveCoerce liftedMethod_showList getLiftedMethod_showList showList
-- | An instance template of @Show2@ for @DerivingVia@
--
-- Example:
--
-- >>> :set -XDerivingVia -XDeriveFunctor -XStandaloneDeriving
-- >>> import Data.Functor.Classes
-- >>> import Data.Bifunctor
-- >>> data F a b = F a b deriving (Show, Functor)
-- >>> instance Bifunctor F where bimap f g (F x y) = F (f x) (g y)
-- >>> deriving via (DerivingShow2 F) instance Show2 F
-- >>> deriving via (DerivingShow2 F a) instance Show a => Show1 (F a)
-- >>> showsPrec2 0 (F (F True 'c') "str") ""
-- "F (F True 'c') \"str\""
-- >>> showsPrec1 0 (F (F True 'c') "str") ""
-- "F (F True 'c') \"str\""
-- >>> showsPrec 0 (F (F True 'c') "str") ""
-- "F (F True 'c') \"str\""
--
newtype DerivingShow2 f a b = DerivingShow2 (f a b)
deriving (Functor, Bifunctor)
instance Show (f a b) => Show (DerivingShow2 f a b) where
showsPrec = coerce $ showsPrec @(f a b)
showList = coerce $ showList @(f a b)
show = coerce $ show @(f a b)
instance (Bifunctor f, Show a, forall x y. (Show x, Show y) => Show (f x y)) => Show1 (DerivingShow2 f a) where
liftShowsPrec = liftShowsPrec2 showsPrec showList
liftShowList = liftShowList2 showsPrec showList
instance (Bifunctor f, forall x y. (Show x, Show y) => Show (f x y)) => Show2 (DerivingShow2 f) where
liftShowsPrec2 _showsPrecX _showListX _showsPrecY _showListY =
case (fromReprDict $ ShowDict _showsPrecX _showListX, fromReprDict $ ShowDict _showsPrecY _showListY) of
(Dict, Dict) -> innerDictReceiveCoerce BiliftedMethod_showsPrec getBiliftedMethod_showsPrec showsPrec
liftShowList2 _showsPrecX _showListX _showsPrecY _showListY =
case (fromReprDict $ ShowDict _showsPrecX _showListX, fromReprDict $ ShowDict _showsPrecY _showListY) of
(Dict, Dict) -> innerDictReceiveCoerce BiliftedMethod_showList getBiliftedMethod_showList showList
-- utilities
newtype Gift a r = Gift (Given a => r)
reifyGift :: Gift a r -> a -> r
reifyGift (Gift f) x = give x f
{-# INLINE reifyGift #-}
class Bicontravariant f where
bicontramap :: (a -> b) -> (c -> d) -> f b d -> f a c
bicontramapCoerce :: (Bicontravariant f, Coercible a b, Coercible c d) => f b d -> f a c
bicontramapCoerce = unsafeCoerce
{-# INLINE bicontramapCoerce #-}
innerBicontramapCoerce :: forall f a b c d fac fbd.
( Bicontravariant f
, Coercible (f a c) fac, Coercible (f b d) fbd
, Coercible a b, Coercible c d
) => (fac -> f a c) -> (f b d -> fbd) -> fac -> fbd
innerBicontramapCoerce _ _ = coerce $ bicontramapCoerce @f @b @a @d @c
{-# INLINE innerBicontramapCoerce #-}
newtype Joker f a b = Joker (f b)
instance Functor f => Bifunctor (Joker f) where
bimap _ lf = coerce $ fmap @f lf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment