{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}
module Data.Dependent.Sum where
import Control.Applicative
import Data.Constraint.Extras
import Data.Type.Equality ((:~:) (..))
import Data.GADT.Show
import Data.GADT.Compare
import Data.Maybe (fromMaybe)
import Text.Read
data DSum tag f = forall a. !(tag a) :=> f a
infixr 1 :=>, ==>
(==>) :: Applicative f => tag a -> a -> DSum tag f
k ==> v = k :=> pure v
instance forall tag f. (GShow tag, Has' Show tag f) => Show (DSum tag f) where
showsPrec p (tag :=> value) = showParen (p >= 10)
( gshowsPrec 0 tag
. showString " :=> "
. has' @Show @f tag (showsPrec 1 value)
)
instance forall tag f. (GRead tag, Has' Read tag f) => Read (DSum tag f) where
readsPrec p = readParen (p > 1) $ \s ->
concat
[ getGReadResult withTag $ \tag ->
[ (tag :=> val, rest'')
| (val, rest'') <- has' @Read @f tag (readsPrec 1 rest')
]
| (withTag, rest) <- greadsPrec p s
, let (con, rest') = splitAt 5 rest
, con == " :=> "
]
instance forall tag f. (GEq tag, Has' Eq tag f) => Eq (DSum tag f) where
(t1 :=> x1) == (t2 :=> x2) = fromMaybe False $ do
Refl <- geq t1 t2
return $ has' @Eq @f t1 (x1 == x2)
instance forall tag f. (GCompare tag, Has' Eq tag f, Has' Ord tag f) => Ord (DSum tag f) where
compare (t1 :=> x1) (t2 :=> x2) = case gcompare t1 t2 of
GLT -> LT
GGT -> GT
GEQ -> has' @Eq @f t1 $ has' @Ord @f t1 (x1 `compare` x2)
{-# DEPRECATED ShowTag "Instead of 'ShowTag tag f', use '(GShow tag, Has' Show tag f)'" #-}
type ShowTag tag f = (GShow tag, Has' Show tag f)
showTaggedPrec :: forall tag f a. (GShow tag, Has' Show tag f) => tag a -> Int -> f a -> ShowS
showTaggedPrec tag = has' @Show @f tag showsPrec
{-# DEPRECATED ReadTag "Instead of 'ReadTag tag f', use '(GRead tag, Has' Read tag f)'" #-}
type ReadTag tag f = (GRead tag, Has' Read tag f)
readTaggedPrec :: forall tag f a. (GRead tag, Has' Read tag f) => tag a -> Int -> ReadS (f a)
readTaggedPrec tag = has' @Read @f tag readsPrec
{-# DEPRECATED EqTag "Instead of 'EqTag tag f', use '(GEq tag, Has' Eq tag f)'" #-}
type EqTag tag f = (GEq tag, Has' Eq tag f)
eqTaggedPrec :: forall tag f a. (GEq tag, Has' Eq tag f) => tag a -> tag a -> f a -> f a -> Bool
eqTaggedPrec tag1 tag2 f1 f2 = case tag1 `geq` tag2 of
Nothing -> False
Just Refl -> has' @Eq @f tag1 $ f1 == f2
eqTagged :: forall tag f a. EqTag tag f => tag a -> tag a -> f a -> f a -> Bool
eqTagged k _ x0 x1 = has' @Eq @f k (x0 == x1)
{-# DEPRECATED OrdTag "Instead of 'OrdTag tag f', use '(GCompare tag, Has' Eq tag f, Has' Ord tag f)'" #-}
type OrdTag tag f = (GCompare tag, Has' Eq tag f, Has' Ord tag f)
compareTaggedPrec :: forall tag f a. (GCompare tag, Has' Eq tag f, Has' Ord tag f) => tag a -> tag a -> f a -> f a -> Ordering
compareTaggedPrec tag1 tag2 f1 f2 = case tag1 `gcompare` tag2 of
GLT -> LT
GEQ -> has' @Eq @f tag1 $ has' @Ord @f tag1 $ f1 `compare` f2
GGT -> GT
compareTagged :: forall tag f a. OrdTag tag f => tag a -> tag a -> f a -> f a -> Ordering
compareTagged k _ x0 x1 = has' @Eq @f k $ has' @Ord @f k (compare x0 x1)