{-# LANGUAGE GADTs, ExistentialQuantification, EmptyDataDecls #-}

module SList where

data Safe
data Unsafe

-- Reimplemention of standard list constructors
-- instead of wrapping
-- Also switching the order of arguments to make 
-- casting easier.
data List a b where
 Nil  :: List Unsafe b
 Cons :: b ->(List c b) -> List Safe b

instance Show b => Show (List a b) where
  show (Nil ) = "nil"
  show (Cons  x xs) = "(cons " ++ show x ++ " " ++ show xs ++ ")"


-- These are all fairly useless. Standard constructors and pattern matching work 
-- better
{-
nil :: List Unsafe b
nil = Nil

cons :: b -> List a b -> List Safe b
cons = Cons

testnull :: List b a -> (List b Unsafe -> c) -> (List b Safe -> c) -> c
testnull l@(Nil )     is isnot = is l
testnull l@(Cons _ _) is isnot = isnot l
-}

-- For flexibilty, note we don't want the opposite conversion

-- Can't do this one.
-- forget :: List Safe b -> List Unsafe b
-- instead, can only do this one.
data ListUnsafe b = forall a. L (List a b)

-- unL :: ListUnsafe b -> exists a. List a b
-- unL (L l) = l

forget :: List Safe b -> ListUnsafe b
forget = L

instance Show a => Show (ListUnsafe a) where
  show (L l) = show l

toList :: List a b -> [b]
toList (Nil ) = []
toList (Cons hd tl) = hd : toList tl

fromList :: [b] -> ListUnsafe b
fromList l = foldr (\x (L xs) -> L (Cons x xs)) (L Nil) l 

-- Four operations that require non-empty lists

sHead :: List Safe b -> b
sHead (Cons hd tl) = hd

sTail :: List Safe b -> ListUnsafe b
sTail (Cons hd tl) = L tl

sLast :: List Safe b -> b
-- from prelude
-- last [x] = x
---last (x : xs) = last xs
sLast (Cons x Nil) = x
sLast (Cons x xs@(Cons _ _)) = sLast xs

sInit :: List Safe a  -> ListUnsafe a 
sInit (Cons x Nil) = L Nil
sInit (Cons x xs@(Cons _ _)) = 
--   let (L l) = sInit xs in 
--   L (Cons x l)
   case (sInit xs) of 
     L l -> L (Cons x l)

-- Other standard operations that work for any list

sElem :: Eq b => b -> List a b -> Bool
sElem b (Cons x xs) = if b == x then True else sElem b xs
sElem b (Nil) = False

sMap  :: (b1 -> b2) -> List a b1 -> List a b2
sMap f (Nil) = Nil
sMap f (Cons x xs) = Cons (f x) (sMap f xs)

sFilter :: (b -> Bool) -> List a b -> ListUnsafe b
sFilter = undefined 

sLength :: List a b -> Int
sLength Nil  = 0
sLength (Cons x xs) = 1 + sLength xs

-- what is type of (binary) concat??
sConcat :: List a1 b -> List a2 b -> ListUnsafe b
sConcat Nil l = L l
sConcat (Cons x xs) l = case (sConcat xs l) of 
                                L l -> L (Cons x l)
