{-# OPTIONS -fno-implicit-prelude #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Dynamic
-- Copyright   :  (c) The University of Glasgow 2001
-- License     :  BSD-style (see the file libraries/base/LICENSE)
-- 
-- Maintainer  :  libraries@haskell.org
-- Stability   :  experimental
-- Portability :  portable
--
-- The Dynamic interface provides basic support for dynamic types.
-- 
-- Operations for injecting values of arbitrary type into
-- a dynamically typed value, Dynamic, are provided, together
-- with operations for converting dynamic values into a concrete
-- (monomorphic) type.
-- 
-----------------------------------------------------------------------------

module Data.Dynamic
  (
	-- * The @Dynamic@ type
	Dynamic,	-- abstract, instance of: Show, Typeable

	-- * Converting to and from @Dynamic@
	toDyn,		-- :: Typeable a => a -> Dynamic
	fromDyn,	-- :: Typeable a => Dynamic -> a -> a
	fromDynamic,	-- :: Typeable a => Dynamic -> Maybe a
	
	-- * Applying functions of dynamic type
	dynApply,
	dynApp,

	-- * Concrete Type Representations
	
	-- | This section is useful if you need to define your own
	-- instances of 'Typeable'.

	Typeable(
	     typeOf),	-- :: a -> TypeRep

	-- ** Building concrete type representations
	TypeRep,	-- abstract, instance of: Eq, Show, Typeable
	TyCon,		-- abstract, instance of: Eq, Show, Typeable

	mkTyCon,	-- :: String  -> TyCon
	mkAppTy,	-- :: TyCon   -> [TypeRep] -> TypeRep
	mkFunTy,	-- :: TypeRep -> TypeRep   -> TypeRep
	applyTy,	-- :: TypeRep -> TypeRep   -> Maybe TypeRep

	-- 
	-- let fTy = mkTyCon "Foo" in show (mkAppTy (mkTyCon ",,")
	--                                 [fTy,fTy,fTy])
	-- 
	-- returns "(Foo,Foo,Foo)"
	--
	-- The TypeRep Show instance promises to print tuple types
	-- correctly. Tuple type constructors are specified by a 
	-- sequence of commas, e.g., (mkTyCon ",,,,") returns
	-- the 5-tuple tycon.
	) where

import Data.Maybe
import Data.Either
import Data.Int
import Data.Word
import Foreign.Ptr
import Foreign.StablePtr

import Hugs.IO
import Hugs.IORef
import Hugs.IOExts

{-|
  A value of type 'Dynamic' is an object encapsulated together with its type.

  A 'Dynamic' may only represent a monomorphic value; an attempt to
  create a value of type 'Dynamic' from a polymorphically-typed
  expression will result in an ambiguity error (see 'toDyn').

  'Show'ing a value of type 'Dynamic' returns a pretty-printed representation
  of the object\'s type; useful for debugging.
-}
data Dynamic = Dynamic TypeRep Obj

instance Show Dynamic where
   -- the instance just prints the type representation.
   showsPrec _ (Dynamic t _) = 
          showString "<<" . 
	  showsPrec 0 t   . 
	  showString ">>"

data Obj = Obj  
 -- dummy type to hold the dynamically typed value.

-- | A concrete representation of a (monomorphic) type.  'TypeRep'
-- supports reasonably efficient equality.
data TypeRep
 = App TyCon   [TypeRep] 
 | Fun TypeRep TypeRep
   deriving ( Eq )

instance Show TypeRep where
  showsPrec p (App tycon tys) =
    case tys of
      [] -> showsPrec p tycon
      [x] | tycon == listTc    -> showChar '[' . shows x . showChar ']'
      xs  
        | isTupleTyCon tycon -> showTuple tycon xs
	| otherwise	     ->
	    showParen (p > 9) $
   	    showsPrec p tycon . 
	    showChar ' '      . 
	    showArgs tys

  showsPrec p (Fun f a) =
     showParen (p > 8) $
     showsPrec 9 f . showString " -> " . showsPrec 8 a

-- | An abstract representation of a type constructor.  'TyCon' objects can
-- be built using 'mkTyCon'.
data TyCon = TyCon Int String

instance Eq TyCon where
  (TyCon t1 _) == (TyCon t2 _) = t1 == t2

instance Show TyCon where
  showsPrec _ (TyCon _ s) = showString s

-- | Converts an arbitrary value into an object of type 'Dynamic'.  
--
-- The type of the object must be an instance of 'Typeable', which
-- ensures that only monomorphically-typed objects may be converted to
-- 'Dynamic'.  To convert a polymorphic object into 'Dynamic', give it
-- a monomorphic type signature.  For example:
--
-- >    toDyn (id :: Int -> Int)
--
toDyn :: Typeable a => a -> Dynamic
toDyn v = Dynamic (typeOf v) (unsafeCoerce v)

-- | Converts a 'Dynamic' object back into an ordinary Haskell value of
-- the correct type.  See also 'fromDynamic'.
fromDyn :: Typeable a
 	=> Dynamic 	-- ^ the dynamically-typed object
	-> a		-- ^ a default value 
	-> a		-- ^ returns: the value of the first argument, if
			-- it has the correct type, otherwise the value of
			-- the second argument.
fromDyn (Dynamic t v) def
  | typeOf def == t = unsafeCoerce v
  | otherwise       = def

-- | Converts a 'Dynamic' object back into an ordinary Haskell value of
-- the correct type.  See also 'fromDyn'.
fromDynamic
	:: Typeable a
	=> Dynamic	-- ^ the dynamically-typed object
	-> Maybe a	-- ^ returns: @'Just' a@, if the dyanmically-typed
			-- object has the correct type (and @a@ is its value), 
			-- or 'Nothing' otherwise.
fromDynamic (Dynamic t v) =
  case unsafeCoerce v of 
    r | t == typeOf r -> Just r
      | otherwise     -> Nothing

-- | The class 'Typeable' allows a concrete representation of a type to
-- be calculated.
class Typeable a where
  typeOf :: a -> TypeRep
  -- ^ Takes a value of type @a@ and returns a concrete representation
  -- of that type.  The /value/ of the argument should be ignored by
  -- any instance of 'Typeable', so that it is safe to pass 'undefined' as
  -- the argument.

isTupleTyCon :: TyCon -> Bool
isTupleTyCon (TyCon _ (',':_)) = True
isTupleTyCon _		       = False

-- If we enforce the restriction that there is only one
-- @TyCon@ for a type & it is shared among all its uses,
-- we can map them onto Ints very simply. The benefit is,
-- of course, that @TyCon@s can then be compared efficiently.

-- Provided the implementor of other @Typeable@ instances
-- takes care of making all the @TyCon@s CAFs (toplevel constants),
-- this will work. 

-- If this constraint does turn out to be a sore thumb, changing
-- the Eq instance for TyCons is trivial.

-- | Builds a 'TyCon' object representing a type constructor.  An
-- implementation of "Data.Dynamic" should ensure that the following holds:
--
-- >  mkTyCon "a" == mkTyCon "a"
--
-- NOTE: GHC\'s implementation is quite hacky, and the above equation
-- does not necessarily hold.  For defining your own instances of
-- 'Typeable', try to ensure that only one call to 'mkTyCon' exists
-- for each type constructor (put it at the top level, and annotate the
-- corresponding definition with a @NOINLINE@ pragma).
mkTyCon
	:: String	-- ^ the name of the type constructor (should be unique
			-- in the program, so it might be wise to use the
			-- fully qualified name).
	-> TyCon	-- ^ A unique 'TyCon' object
mkTyCon str = unsafePerformIO $ do
   v <- readIORef uni
   writeIORef uni (v+1)
   return (TyCon v str)

{-# NOINLINE uni #-}
uni :: IORef Int
uni = unsafePerformIO ( newIORef 0 )

-- Some (Show.TypeRep) helpers:

showArgs :: Show a => [a] -> ShowS
showArgs [] = id
showArgs [a] = showsPrec 10 a
showArgs (a:as) = showsPrec 10 a . showString " " . showArgs as 

showTuple :: TyCon -> [TypeRep] -> ShowS
showTuple (TyCon _ str) args = showChar '(' . go str args
 where
  go [] [a] = showsPrec 10 a . showChar ')'
  go _  []  = showChar ')' -- a failure condition, really.
  go (',':xs) (a:as) = showsPrec 10 a . showChar ',' . go xs as
  go _ _   = showChar ')'

-- | Applies a type constructor to a sequence of types
mkAppTy  :: TyCon   -> [TypeRep] -> TypeRep
mkAppTy tyc args = App tyc args

-- | A special case of 'mkAppTy', which applies the function type constructor to
-- a pair of types.
mkFunTy  :: TypeRep -> TypeRep   -> TypeRep
mkFunTy f a = Fun f a

-- Auxillary functions

-- (f::(a->b)) `dynApply` (x::a) = (f a)::b
dynApply :: Dynamic -> Dynamic -> Maybe Dynamic
dynApply (Dynamic t1 f) (Dynamic t2 x) =
  case applyTy t1 t2 of
    Just t3 -> Just (Dynamic t3 ((unsafeCoerce f) x))
    Nothing -> Nothing

dynApp :: Dynamic -> Dynamic -> Dynamic
dynApp f x = case dynApply f x of 
             Just r -> r
             Nothing -> error ("Type error in dynamic application.\n" ++
                               "Can't apply function " ++ show f ++
                               " to argument " ++ show x)

-- | Applies a type to a function type.  Returns: @'Just' u@ if the
-- first argument represents a function of type @t -> u@ and the
-- second argument represents a function of type @t@.  Otherwise,
-- returns 'Nothing'.
applyTy :: TypeRep -> TypeRep -> Maybe TypeRep
applyTy (Fun t1 t2) t3
  | t1 == t3    = Just t2
applyTy _ _     = Nothing

-- Prelude types

listTc :: TyCon
listTc = mkTyCon "[]"

instance Typeable a => Typeable [a] where
  typeOf ls = mkAppTy listTc [typeOf ((undefined:: [a] -> a) ls)]

unitTc :: TyCon
unitTc = mkTyCon "()"

instance Typeable () where
  typeOf _ = mkAppTy unitTc []

tup2Tc :: TyCon
tup2Tc = mkTyCon ","

instance (Typeable a, Typeable b) => Typeable (a,b) where
  typeOf tu = mkAppTy tup2Tc [typeOf ((undefined :: (a,b) -> a) tu),
			      typeOf ((undefined :: (a,b) -> b) tu)]

tup3Tc :: TyCon
tup3Tc = mkTyCon ",,"

instance ( Typeable a , Typeable b , Typeable c) => Typeable (a,b,c) where
  typeOf tu = mkAppTy tup3Tc [typeOf ((undefined :: (a,b,c) -> a) tu),
			      typeOf ((undefined :: (a,b,c) -> b) tu),
			      typeOf ((undefined :: (a,b,c) -> c) tu)]

tup4Tc :: TyCon
tup4Tc = mkTyCon ",,,"

instance ( Typeable a
	 , Typeable b
	 , Typeable c
	 , Typeable d) => Typeable (a,b,c,d) where
  typeOf tu = mkAppTy tup4Tc [typeOf ((undefined :: (a,b,c,d) -> a) tu),
			      typeOf ((undefined :: (a,b,c,d) -> b) tu),
			      typeOf ((undefined :: (a,b,c,d) -> c) tu),
			      typeOf ((undefined :: (a,b,c,d) -> d) tu)]

tup5Tc :: TyCon
tup5Tc = mkTyCon ",,,,"

instance ( Typeable a
	 , Typeable b
	 , Typeable c
	 , Typeable d
	 , Typeable e) => Typeable (a,b,c,d,e) where
  typeOf tu = mkAppTy tup5Tc [typeOf ((undefined :: (a,b,c,d,e) -> a) tu),
			      typeOf ((undefined :: (a,b,c,d,e) -> b) tu),
			      typeOf ((undefined :: (a,b,c,d,e) -> c) tu),
			      typeOf ((undefined :: (a,b,c,d,e) -> d) tu),
			      typeOf ((undefined :: (a,b,c,d,e) -> e) tu)]

instance (Typeable a, Typeable b) => Typeable (a -> b) where
  typeOf f = mkFunTy (typeOf ((undefined :: (a -> b) -> a) f))
		     (typeOf ((undefined :: (a -> b) -> b) f))

boolTc = mkTyCon "Bool"; instance Typeable Bool where { typeOf _ = mkAppTy boolTc [] }
charTc = mkTyCon "Char"; instance Typeable Char where { typeOf _ = mkAppTy charTc [] }
floatTc = mkTyCon "Float"; instance Typeable Float where { typeOf _ = mkAppTy floatTc [] }
doubleTc = mkTyCon "Double"; instance Typeable Double where { typeOf _ = mkAppTy doubleTc [] }
intTc = mkTyCon "Int"; instance Typeable Int where { typeOf _ = mkAppTy intTc [] }
integerTc = mkTyCon "Integer"; instance Typeable Integer where { typeOf _ = mkAppTy integerTc [] }
eitherTc = mkTyCon "Either"; instance (Typeable a, Typeable b) => Typeable (Either a b) where { typeOf x = mkAppTy eitherTc [typeOf ((undefined :: Either a b -> a) x), typeOf ((undefined :: Either a b -> b) x)] }
ioTc = mkTyCon "IO"; instance Typeable a => Typeable (IO a) where { typeOf x = mkAppTy ioTc [typeOf ((undefined :: IO a -> a) x) ] }
maybeTc = mkTyCon "Maybe"; instance Typeable a => Typeable (Maybe a) where { typeOf x = mkAppTy maybeTc [typeOf ((undefined :: Maybe a -> a) x) ] }
orderingTc = mkTyCon "Ordering"; instance Typeable Ordering where { typeOf _ = mkAppTy orderingTc [] }
handleTc = mkTyCon "Handle"; instance Typeable Handle where { typeOf _ = mkAppTy handleTc [] }
ptrTc = mkTyCon "Ptr"; instance Typeable a => Typeable (Ptr a) where { typeOf x = mkAppTy ptrTc [typeOf ((undefined :: Ptr a -> a) x) ] }
stablePtrTc = mkTyCon "StablePtr"; instance Typeable a => Typeable (StablePtr a) where { typeOf x = mkAppTy stablePtrTc [typeOf ((undefined :: StablePtr a -> a) x) ] }

int8Tc = mkTyCon  "Int8"; instance Typeable Int8 where { typeOf _ = mkAppTy int8Tc [] }
int16Tc = mkTyCon "Int16"; instance Typeable Int16 where { typeOf _ = mkAppTy int16Tc [] }
int32Tc = mkTyCon "Int32"; instance Typeable Int32 where { typeOf _ = mkAppTy int32Tc [] }
int64Tc = mkTyCon "Int64"; instance Typeable Int64 where { typeOf _ = mkAppTy int64Tc [] }

word8Tc = mkTyCon  "Word8" ; instance Typeable Word8 where { typeOf _ = mkAppTy word8Tc [] }
word16Tc = mkTyCon "Word16"; instance Typeable Word16 where { typeOf _ = mkAppTy word16Tc [] }
word32Tc = mkTyCon "Word32"; instance Typeable Word32 where { typeOf _ = mkAppTy word32Tc [] }
word64Tc = mkTyCon "Word64"; instance Typeable Word64 where { typeOf _ = mkAppTy word64Tc [] }

tyconTc = mkTyCon "TyCon"; instance Typeable TyCon where { typeOf _ = mkAppTy tyconTc [] }
typeRepTc = mkTyCon "TypeRep"; instance Typeable TypeRep where { typeOf _ = mkAppTy typeRepTc [] }
dynamicTc = mkTyCon "Dynamic"; instance Typeable Dynamic where { typeOf _ = mkAppTy dynamicTc [] }

ioRefTc = mkTyCon "IORef"; instance Typeable a => Typeable (IORef a) where { typeOf x = mkAppTy ioRefTc [typeOf ((undefined :: IORef a -> a) x) ] }

