haskell-distributed/rank1dynamic — pull request #7 by nick8325 (2014-08-18 12:41:30+00:00)
Title: Change TypVar from an empty datatype to a newtype-wrapped Any.

The following module defines a function `test` of type `a -> b` which evaluates its argument and then crashes with `undefined`:

```haskell
import Data.Rank1Typeable
import Data.Rank1Dynamic

test :: ANY -> ANY1
test x = x `seq` undefined

oops :: (Typeable a, Typeable b) => a -> b
oops x = fromRight (toDynamic test `dynApply` toDynamic x >>= fromDynamic)
where
fromRight (Right x) = x
```

`oops x` is simply doing `test x` but with everything wrapped in `Dynamic`. Evaluating, say, `oops False :: Int` should clearly crash, because `test False = undefined`, but instead we get:

```
*Main> oops False :: Int
140055520632632
```

What is happening here? Looking at the generated core shows what's going on:

```
Test.test :: Data.Rank1Typeable.ANY -> Data.Rank1Typeable.ANY1
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType b]
Test.test =
\ (x_aHK :: Data.Rank1Typeable.ANY) ->
case x_aHK of _ [Occ=Dead] { }
```

GHC has compiled the `seq` into a case analysis on `x`, which is of type `ANY`, a synonym for `TypVar V0`. However, `TypVar` is defined as a datatype with no constructors, so the generated case-expression has no cases! This is perfectly valid code given that `test :: ANY -> ANY1`, but then `Data.Rank1Dynamic` comes along and casts `test` to `Bool -> Int`, and the core above obviously cannot work.

The solution is not to define `TypVar` as an empty datatype, but instead to define it as a newtype wrapper for `GHC.Exts.Any`, which gives us permission to instantiate `TypVar`s with whatever we like. The attached patch does this, and now `test` behaves as it should:

```
*Test> oops False :: Int
*** Exception: Prelude.undefined
```