Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing an instance declaration for Typeable without deriving

How do I write an actual instance declaration for Typeable? For instance, say I wanted to write an instance for Char where each character had a different type, e.g.

instance Typeable Char where
    typeOf 'a' = C
    typeOf 'b' = C -> D
    typeOf 'c' = D -> E
    typeOf     = String

Obviously this wouldn't be written like this because the output of typeOf is a TypeRep, but I can't figure out how to actually construct a TypeRep.

Is this even possible? It seems like everything that talks about Typeable assumes that you would use DeriveDataTypeable.

like image 263
thylacine222 Avatar asked Jan 03 '14 03:01

thylacine222


1 Answers

Well there are a few issues with doing this.

  1. It's broken. typeOf isn't supposed to be strict in it's argument so typeOf (undefined :: Char) should work.

  2. It's unsafe. Actually, if you manually create an instance of Typeable you won't compile under Safe Haskell.

Keeping this in mind!

{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable

data C = C deriving (Typeable)
data D = D deriving (Typeable)
data E = E deriving (Typeable)
newtype Foo = Foo Char

instance Typeable Foo where
  typeOf (Foo 'a') = typeOf (undefined :: C)
  typeOf (Foo 'b') = typeOf (undefined :: C -> D)
  typeOf (Foo 'c') = typeOf (undefined :: D -> E)
  typeOf  _        = typeOf (undefined :: String)

Now as an example of why this is terrible, consider

what :: Char -> C
what c = if isJust weird then fromJust weird else error "Sanity!"
  where weird = fromDynamic . toDyn $ Foo c

Now

> what 'a'
  C
> what 'b'
  *** Exception: Sanity!

Depending on the representation of how Foo this could do all sorts of fun things, like seg fault, spew nonsense answers, or make monkeys fly out of your ears.

Robert Harper gave a more dramatic example

{-# LANGUAGE DeriveDataTypeable #-}
import Control.Exception
import Data.Typeable

newtype Foo = Foo (() -> IO ())

{- set Foo’s TypeRep to be the same as ErrorCall’s -}
instance Typeable Foo where
  typeOf _ = typeOf (undefined :: ErrorCall)
instance Show Foo where  show _ = ""
instance Exception Foo

main = Control.Exception.catch (error "kaboom") (\ (Foo f) -> f ())

Which gives

<interactive>: internal error: stg_ap_v_ret
    (GHC version 7.6.3 for x86_64_unknown_linux)
    Please report this as a GHC bug:  http://www.haskell.org/ghc/reportabug
  C-c C-c
Process haskell aborted (core dumped)
like image 74
Daniel Gratzer Avatar answered Sep 19 '22 08:09

Daniel Gratzer