{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Monad
import Data.Dependent.Sum
import Data.Functor.Identity
import Data.Constraint.Extras.TH
import Data.GADT.Compare
import Data.GADT.Compare.TH
import Data.GADT.Show
import Data.GADT.Show.TH
import Data.Type.Equality
data MySum :: * -> * where
MySum_Int :: MySum Int
MySum_String :: MySum String
deriving instance Eq (MySum a)
deriving instance Ord (MySum a)
deriving instance Show (MySum a)
deriveGShow ''MySum
deriveGEq ''MySum
deriveGCompare ''MySum
deriveArgDict ''MySum
data MyNestedSum :: * -> * where
MyNestedSum_MySum :: MySum a -> MyNestedSum a
MyNestedSum_Int :: Int -> MyNestedSum Int
MyNestedSum_String :: [Int] -> MyNestedSum String
deriving instance Eq (MyNestedSum a)
deriving instance Ord (MyNestedSum a)
deriving instance Show (MyNestedSum a)
deriveGShow ''MyNestedSum
deriveGEq ''MyNestedSum
deriveGCompare ''MyNestedSum
deriveArgDict ''MyNestedSum
polyTests
:: forall m f
. ( MonadPlus m, Show (f Int), Show (f String)
, GCompare f, GShow f)
=> (forall a. MySum a -> f a)
-> m ()
polyTests f = do
do
let showSame :: forall a. Show (f a) => f a -> Bool
showSame gadt = show gadt == gshow gadt
guard $ showSame $ f MySum_Int
guard $ showSame $ f MySum_String
guard $ (f MySum_Int `geq` f MySum_Int) == Just Refl
guard $ (f MySum_Int `gcompare` f MySum_Int) == GEQ
guard $ (f MySum_String `geq` f MySum_String) == Just Refl
guard $ (f MySum_String `gcompare` f MySum_String) == GEQ
guard $ (f MySum_Int `gcompare` f MySum_String) == GLT
guard $ (f MySum_String `gcompare` f MySum_Int) == GGT
main :: IO ()
main = do
polyTests id
polyTests MyNestedSum_MySum
return ()
--TODO: Figure out how to best use these test cases; just checking that they
-- compile is useful, but it's probably more useful to check some properties as
-- well
-- test cases: should be able to generate instances for these
-- (Bar requiring the existence of an instance for Foo)
data Foo a where
I :: Foo Int
D :: Foo Double
A :: Foo a -> Foo b -> Foo (a -> b)
data Bar a where
F :: Foo a -> Bar a
S :: Bar String
data Baz a where
L :: Qux a -> Int -> Baz [a]
data Qux a where
FB :: Foo (a -> b) -> Bar b -> Qux (a -> (b, b))
deriveGEq ''Foo
deriveGEq ''Bar
deriveGEq ''Baz
deriveGEq ''Qux
deriveGCompare ''Foo
deriveGCompare ''Bar
deriveGCompare ''Baz
deriveGCompare ''Qux
instance Show (Foo a) where showsPrec = gshowsPrec
instance Show (Bar a) where showsPrec = gshowsPrec
instance Show (Baz a) where showsPrec = gshowsPrec
instance Show (Qux a) where showsPrec = gshowsPrec
deriveGShow ''Foo
deriveGShow ''Bar
deriveGShow ''Baz
deriveGShow ''Qux
data Squudge a where
E :: Ord a => Foo a -> Squudge a
deriveGEq ''Squudge
deriveGCompare ''Squudge
deriveGShow ''Squudge
instance Show (Squudge a) where showsPrec = gshowsPrec
data Splort a where
Splort :: Squudge a -> a -> Splort a
-- -- deriveGEq ''Splort
-- This one theoretically could work (instance explicitly given below), but I don't think
-- it's something I want to try to automagically support. It would require actually
-- matching on sub-constructors, which could get pretty ugly, especially since it may
-- not even be the case that a finite number of matches would suffice.
instance GEq Splort where
geq (Splort (E x1) x2) (Splort (E y1) y2) = do
Refl <- geq x1 y1
guard (x2 == y2)
Just Refl
deriving instance Show a => Show (Splort a)
instance GCompare Splort where
gcompare (Splort (E x1) x2) (Splort (E y1) y2) =
runGComparing $ do
Refl <- geq' x1 y1
compare' x2 y2
return GEQ
-- Also should work for empty types
data Empty a
deriveGEq ''Empty
deriveGCompare ''Empty
-- Also supports types with multiple parameters, by quoting empty instance declarations
-- ([t||] brackets won't work because they can only quote types of kind *).
data Spleeb a b where
P :: a Double -> Qux b -> Spleeb a b
-- need a cleaner 'one-shot' way of defining these - the empty instances need to appear
-- in the same quotation because the GEq context of the GCompare class causes stage
-- restriction errors... seems like GHC shouldn't actually check things like that till
-- the final splice, but whatever.
do
[geqInst, gcompareInst, gshowInst] <-
[d|
instance GEq a => GEq (Spleeb a)
instance GCompare a => GCompare (Spleeb a)
instance Show (a Double) => GShow (Spleeb a)
|]
concat <$> sequence
[ deriveGEq geqInst
, deriveGCompare gcompareInst
, deriveGShow gshowInst
]
instance Show (a Double) => Show (Spleeb a b) where showsPrec = gshowsPrec
-- another option; start from the declaration and juggle that a bit
do
decs <- [d|
data Fnord a where Yarr :: Fnord Double; Grr :: Fnord (Int -> String)
|]
geqInst <- deriveGEq decs
gcompareInst <- deriveGCompare decs
gshowInst <- deriveGShow decs
return $ concat
[ decs
, geqInst
, gcompareInst
, gshowInst
]
instance Show (Fnord a) where showsPrec = gshowsPrec