%
% (c) AQUA Project, Glasgow University, 1998
%

Cheap and cheerful dynamic types.

The Dynamic interface is part of the Hugs/GHC standard
libraries, providing 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.

The Dynamic implementation provided is closely based on code
contained in Hugs library of the same name.

NOTE: test code at the end, but commented out.

\begin{code}
module Dynamic
    (
      -- dynamic type
      Dynamic	  -- abstract, instance of: Show (?)
    , toDyn       -- :: Typeable a => a -> Dynamic
    , fromDyn	  -- :: Typeable a => Dynamic -> a -> a
    , fromDynamic -- :: Typeable a => Dynamic -> Maybe a
	
      -- type representation

    , Typeable(typeOf) 
      -- class Typeable a where { typeOf :: a -> TypeRep }

      -- Dynamic defines Typeable instances for the following
      -- Prelude types: Char, Int, Float, Double, Bool
      --                (), Maybe a, (a->b), [a]
      --		(a,b) (a,b,c) (a,b,c,d) (a,b,c,d,e)

    , TypeRep      -- abstract, instance of: Eq, Show
    , TyCon        -- abstract, instance of: Eq, Show

      -- type representation constructors/operators:
    , mkTyCon	   -- :: String  -> TyCon
    , mkAppTy	   -- :: TyCon   -> [TypeRep] -> TypeRep
    , mkFunTy      -- :: TypeRep -> TypeRep   -> TypeRep
    , applyTy	   -- :: TypeRep -> TypeRep   -> Maybe TypeRep

      -- 
      -- let iTy = mkTyCon "Int" in show (mkAppTy (mkTyCon ",,")
      --                                 [iTy,iTy,iTy])
      -- 
      -- returns "(Int,Int,Int)"
      --
      -- 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 7-tuple tycon.
    ) where

{- BEGIN_FOR_GHC
import GlaExts
import PrelDynamic
   END_FOR_GHC -}

import IOExts 
       ( unsafePerformIO,
         IORef, newIORef, readIORef, writeIORef
        )

{- BEGIN_FOR_HUGS -}
data TypeRep
 = App TyCon   [TypeRep]
 | Fun TypeRep TypeRep
   deriving ( Eq )

-- type constructors are 
data TyCon = TyCon Int String

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

data Dynamic = Dynamic TypeRep Obj

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

primitive unsafeCoerce "primUnsafeCoerce" :: a -> b
{- END_FOR_HUGS -}

{- BEGIN_FOR_GHC
unsafeCoerce :: a -> b
unsafeCoerce = unsafeCoerce#
   END_FOR_GHC -}
\end{code}

The dynamic type is represented by Dynamic, carrying
the dynamic value along with its type representation:

\begin{code}
-- the instance just prints the type representation.
instance Show Dynamic where
   showsPrec _ (Dynamic t _) = 
          showString "<<" . 
	  showsPrec 0 t   . 
	  showString ">>"
\end{code}

Operations for going to and from Dynamic:

\begin{code}
toDyn :: Typeable a => a -> Dynamic
toDyn v = Dynamic (typeOf v) (unsafeCoerce v)

fromDyn :: Typeable a => Dynamic -> a -> a
fromDyn (Dynamic t v) def
  | typeOf def == t = unsafeCoerce v
  | otherwise       = def

fromDynamic :: Typeable a => Dynamic -> Maybe a
fromDynamic (Dynamic t v) =
  case unsafeCoerce v of 
    r | t == typeOf r -> Just r
      | otherwise     -> Nothing
\end{code}

(Abstract) universal datatype:

\begin{code}
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
\end{code}

To make it possible to convert values with user-defined types
into type Dynamic, we need a systematic way of getting
the type representation of an arbitrary type. Type class
provide a good fit, here

\begin{code}
class Typeable a where
  typeOf :: a -> TypeRep
\end{code}

NOTE: The argument to the overloaded `typeOf' is only
used to carry type information, and Typeable instances
should *never* look at its value.

\begin{code}
isTupleTyCon :: TyCon -> Bool
isTupleTyCon (TyCon _ (',':_)) = True
isTupleTyCon _		       = False

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

-- 
-- If we enforce the restriction that TyCons are
-- shared, we can map them onto Ints very simply
-- which allows for efficient comparison.
--
mkTyCon :: String -> TyCon
mkTyCon str = unsafePerformIO $ do
   v <- readIORef uni
   writeIORef uni (v+1)
   return (TyCon v str)

uni :: IORef Int
uni = unsafePerformIO ( newIORef 0 )
\end{code}

Some (Show.TypeRep) helpers:

\begin{code}
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 ')'
\end{code}

\begin{code}
mkAppTy  :: TyCon   -> [TypeRep] -> TypeRep
mkAppTy tyc args = App tyc args

mkFunTy  :: TypeRep -> TypeRep   -> TypeRep
mkFunTy f a = Fun f a
\end{code}

Auxillary functions

\begin{code}
-- (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)

applyTy :: TypeRep -> TypeRep -> Maybe TypeRep
applyTy (Fun t1 t2) t3
  | t1 == t3    = Just t2
applyTy _ _     = Nothing

\end{code}

\begin{code}
instance Typeable Int where
  typeOf _ = mkAppTy intTc []
  
instance Typeable Char where
  typeOf _ = mkAppTy charTc []
  
instance Typeable Bool where
  typeOf _ = mkAppTy boolTc []
  
instance Typeable Float where
  typeOf _ = mkAppTy floatTc []
  
instance Typeable Double where
  typeOf _ = mkAppTy doubleTc []

instance Typeable Integer where
  typeOf _ = mkAppTy integerTc []

instance Typeable a => Typeable (IO a) where
  typeOf action = mkAppTy ioTc [typeOf (doIO action)]
    where
      doIO :: IO a -> a
      doIO = undefined

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

instance Typeable a => Typeable (Maybe a) where
  typeOf mb = mkAppTy maybeTc [typeOf (getJ mb)]
    where
      getJ :: Maybe a -> a
      getJ = undefined

instance (Typeable a, Typeable b) => Typeable (Either a b) where
  typeOf ei = mkAppTy maybeTc [typeOf (getL ei), typeOf (getR ei)]
    where
      getL :: Either a b -> a
      getL = undefined
      getR :: Either a b -> a
      getR = undefined

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

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

instance Typeable TypeRep where
  typeOf _ = mkAppTy typeRepTc []

instance Typeable TyCon where
  typeOf _ = mkAppTy tyConTc []

instance Typeable Dynamic where
  typeOf _ = mkAppTy dynamicTc []

instance Typeable Ordering where
  typeOf _ = mkAppTy orderingTc []

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

tup2Tc :: TyCon
tup2Tc = mkTyCon ","

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

tup3Tc :: TyCon
tup3Tc = mkTyCon ",,"

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

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

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

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

\end{code}

@TyCon@s are provided for the following:

\begin{code}
-- prelude types:
intTc, charTc, boolTc :: TyCon
intTc      = mkTyCon "Int"
charTc     = mkTyCon "Char"
boolTc     = mkTyCon "Bool"

floatTc, doubleTc, integerTc :: TyCon
floatTc    = mkTyCon "Float"
doubleTc   = mkTyCon "Double"
integerTc  = mkTyCon "Integer"

ioTc, maybeTc, eitherTc, listTc :: TyCon
ioTc       = mkTyCon "IO"
maybeTc    = mkTyCon "Maybe"
eitherTc   = mkTyCon "Either"
listTc     = mkTyCon "[]"

unitTc, orderingTc, arrayTc, complexTc, handleTc :: TyCon
unitTc     = mkTyCon "()"
orderingTc = mkTyCon "Ordering"
arrayTc    = mkTyCon "Array"
complexTc  = mkTyCon "Complex"
handleTc   = mkTyCon "Handle"

-- Hugs/GHC extension lib types:
addrTc, stablePtrTc, mvarTc :: TyCon
addrTc       = mkTyCon "Addr"
stablePtrTc  = mkTyCon "StablePtr"
mvarTc       = mkTyCon "MVar"

foreignObjTc, stTc :: TyCon
foreignObjTc = mkTyCon "ForeignObj"
stTc         = mkTyCon "ST"

int8Tc, int16Tc, int32Tc, int64Tc :: TyCon
int8Tc       = mkTyCon "Int8"
int16Tc      = mkTyCon "Int16"
int32Tc      = mkTyCon "Int32"
int64Tc	     = mkTyCon "Int64"

word8Tc, word16Tc, word32Tc, word64Tc :: TyCon
word8Tc      = mkTyCon "Word8"
word16Tc     = mkTyCon "Word16"
word32Tc     = mkTyCon "Word32"
word64Tc     = mkTyCon "Word64"

tyConTc, typeRepTc, dynamicTc :: TyCon
tyConTc      = mkTyCon "TyCon"
typeRepTc    = mkTyCon "Type"
dynamicTc    = mkTyCon "Dynamic"

-- GHC specific:
{- BEGIN_FOR_GHC
byteArrayTc, mutablebyteArrayTc, wordTc :: TyCon
byteArrayTc  = mkTyCon "ByteArray"
mutablebyteArrayTc = mkTyCon "MutableByteArray"
wordTc       = mkTyCon "Word"
   END_FOR_GHC -}

\end{code}

begin{code}
test1,test2, test3, test4 :: Dynamic

test1 = toDyn (1::Int)
test2 = toDyn ((+) :: Int -> Int -> Int)
test3 = dynApp test2 test1
test4 = dynApp test3 test1

test5, test6,test7 :: Int
test5 = fromDyn test4 0
test6 = fromDyn test1 0
test7 = fromDyn test2 0

test8 :: Dynamic
test8 = toDyn (mkAppTy listTc)

test9 :: Float
test9 = fromDyn test8 0

printf :: String -> [Dynamic] -> IO ()
printf str args = putStr (decode str args)
 where
  decode [] [] = []
  decode ('%':'n':cs) (d:ds) =
    (\ v -> show v++decode cs ds) (fromDyn  d (0::Int))
  decode ('%':'c':cs) (d:ds) =
    (\ v -> show v++decode cs ds) (fromDyn  d ('\0'))
  decode ('%':'b':cs) (d:ds) =
    (\ v -> show v++decode cs ds) (fromDyn  d (False::Bool))
  decode (x:xs) ds = x:decode xs ds

test10 :: IO ()
test10 = printf "%n = %c, that much is %b\n" [toDyn (3::Int),toDyn 'a', toDyn False]
end{code}


