1

I have a type named Showable like this:

    {-# LANGUAGE ExistentialQuantification #-}
    {-# LANGUAGE ExplicitForAll #-}
    data Showable = forall a . Show a => Showable a

Then making a function that packs it is trivial. I just have to write:

    pack :: forall a . Show a => a -> Showable
    pack x = Showable x

However it seems impossible to create the inverse function that would unpack the data from Showable. If I try to just invert what I wrote for pack and write:

    unpack :: exists a . Show a => Showable -> a
    unpack (Showable x) = x

then I get an error from GHC.

I have looked into the docs on GHC Language extensions and there seems to be no support for the exists keyword. I have seen that it might be possible in some other Haskell compilers, but I would prefer to be able to do it in GHC as well.

Funnily enough, I can still pattern match on Showable and extract the data from inside it in that way. So I could get the value out of it that way, but if I wanted to make a pointfree function involving Showable, then I would need unpack.

So is there some way to implement unpack in GHC's Haskell, maybe using Type Families or some other arcane magic that are GHC extensions?

The Red Fox
  • 810
  • 6
  • 12
  • What would `(unpack (pack (1 :: Int))) :: String` be? – Cirdec May 12 '16 at 19:29
  • I think that should be a compile error since you are trying to give type to it after you have packed the value, because after you pack it you lose information about the typo of what is inside, so you can't know if you can change the type of it to String. – The Red Fox May 12 '16 at 19:38

1 Answers1

3

Your unpack, as you have typed, cannot be written. The reason is that the existential variable a "escapes" the scope of the pattern match and there is no facility to track that behavior. Quote the docs:

data Baz = forall a. Eq a => Baz1 a a
         | forall b. Show b => Baz2 b (b -> b)

When pattern matching, each pattern match introduces a new, distinct, type for each existential type variable. These types cannot be unified with any other type, nor can they escape from the scope of the pattern match. For example, these fragments are incorrect:

f1 (MkFoo a f) = a

What is this "a" in the result type? Clearly we don't mean this:

f1 :: forall a. Foo -> a   -- Wrong!

The original program is just plain wrong. Here's another sort of error

 f2 (Baz1 a b) (Baz1 p q) = a==q

It's ok to say a==b or p==q, but a==q is wrong because it equates the two distinct types arising from the two Baz1 constructors.

You can however rewrite the unpack type equivalently so that the existential does not escape:

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RankNTypes                #-}

module Lib where

data Showable = forall a. Show a => Showable a

pack :: Show a => a -> Showable
pack = Showable

unpack :: Showable -> (forall a. Show a => a -> r) -> r
unpack (Showable a) a2r = a2r a
hao
  • 10,138
  • 1
  • 35
  • 50