{-# LANGUAGE GADTs #-}
module FooGADT where

import Data.Dependent.Sum
import Data.GADT.Show
import Data.GADT.Compare

data Foo a where
    Foo :: Foo Double
    Bar :: Foo Int
    Baz :: Foo String
    Qux :: Foo Double

instance Eq (Foo a) where
    (==) = defaultEq

instance GEq Foo where
    geq Foo Foo = Just Refl
    geq Bar Bar = Just Refl
    geq Baz Baz = Just Refl
    geq Qux Qux = Just Refl
    geq _   _   = Nothing

instance EqTag Foo where
    eqTagged Foo Foo = (==)
    eqTagged Bar Bar = (==)
    eqTagged Baz Baz = (==)
    eqTagged Qux Qux = (==)
    eqTagged _   _   = const (const False)

instance GCompare Foo where
    gcompare Foo Foo = GEQ
    gcompare Foo _   = GLT
    gcompare _   Foo = GGT
    
    gcompare Bar Bar = GEQ
    gcompare Bar _   = GLT
    gcompare _   Bar = GGT
    
    gcompare Baz Baz = GEQ
    gcompare Baz _   = GLT
    gcompare _   Baz = GGT
    
    gcompare Qux Qux = GEQ

instance OrdTag Foo where
    compareTagged Foo Foo = compare
    compareTagged Bar Bar = compare
    compareTagged Baz Baz = compare
    compareTagged Qux Qux = compare
    
    compareTagged _   _   = error "OrdTag (Foo): bad case"

instance Show (Foo a) where
    showsPrec _ Foo      = showString "Foo"
    showsPrec _ Bar      = showString "Bar"
    showsPrec _ Baz      = showString "Baz"
    showsPrec _ Qux      = showString "Qux"

instance GShow Foo where
    gshowsPrec = showsPrec

instance ShowTag Foo where
    showTaggedPrec Foo = showsPrec
    showTaggedPrec Bar = showsPrec
    showTaggedPrec Baz = showsPrec
    showTaggedPrec Qux = showsPrec


instance GRead Foo where
    greadsPrec _ str = case tag of
        "Foo" -> [(\k -> k Foo, rest)]
        "Bar" -> [(\k -> k Bar, rest)]
        "Baz" -> [(\k -> k Baz, rest)]
        "Qux" -> [(\k -> k Qux, rest)]
        _     -> []
        where (tag, rest) = splitAt 3 str

instance ReadTag Foo where
    readTaggedPrec Foo = readsPrec
    readTaggedPrec Bar = readsPrec
    readTaggedPrec Baz = readsPrec
    readTaggedPrec Qux = readsPrec


foo x = Foo :=> x
bar x = Bar :=> x
baz x = Baz :=> x
qux x = Qux :=> x


xs = [foo pi, bar 100, baz "hello world", qux (exp 1)]
xs' = read (show xs) `asTypeOf` xs