Dynamic types: GHCI works, GHC doesn't?

Alastair Reid reid@cs.utah.edu
01 Jun 2002 19:56:39 +0100

Andre Pang <ozone@algorithm.com.au> writes:
> Hi all, I'm trying to get a grip on the Dynamic types stuff supplied
> with GHC, and I'm not sure if I'm doing something wrong, or whether
> I've found a bug.

Your Typeable instance looks like this:

> instance Typeable FootnoteData where
>    typeOf _ = mkAppTy (mkTyCon "FootnoteData") [typeOf ("Foo" :: String), typeOf (7 :: Int)]

This should be written:

> instance Typeable FootnoteData where
>    typeOf _ = mkAppTy fdtc [typeOf ("Foo" :: String), typeOf (7 :: Int)]
> fdtc = mkTyCon "FootnoteData"

That is, the TyCon definition has to be a CAF (i.e., a top level
definition with no arguments).  The reason is somewhat grubby but
comes down to 'things go much faster if you do this').  

Here's the relevant comment from the code:

-- 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.

We've seen several bug reports of this form lately - can someone
change the representation/ implementation to something a little less
fragile?  Three possibilities spring to mind:

1) The original slow implementation which uses string comparisions.
   This could be speeded up using unsafePtrEquality in the normal
   (Lisplike) way.
2) Maintain a table of all TyCons (or the stablename table??)
   When a TyCon thunk is evaluated, it looks itself up in the
   TyCon table inserting itself if necessary and updates itself
   with an index into the table.

3) Same as (2) except that if the lookup succeeds it knows there are
   two independent TyCons for the same string.  One of two things must
   have happened:

   1) Two independent types (perhaps in different modules) used the same
      string as their TyCon identifier.  Yoiks!  Report it as an error.

   2) Someone forgot to make their TyCon a CAF.  This could be made to
      work (using implementation strategy 2) but it will be slow.
      Best thing to do is report the problem and have the programmer fix

I favour 1 or 3.

While we're at it, I'd like to repeat my request that the word
'unsafe' be made part of the classname Typeable or the method name
typeOf.  The reason is that a badly written Typeable definition can
break typesafety.  Here's a superficially plausible but broken 
instance for IO which demonstrates it

> instance Typeable (IO a) where
>    typeOf _ = mkAppTy iotc []
> iotc = mkTyCon "IO"

With this definition

  typeOf (return 1 :: IO Int) == typeOf (return () :: IO ())

and so you can coerce back and forth between these types using

[A better fix would be for compilers to generate the (somewhat tricky)
Typeable instances themselves.]

Alastair Reid        reid@cs.utah.edu        http://www.cs.utah.edu/~reid/