简体   繁体   中英

Is there a way to represent the function unpack on Showable in GHC's Haskell?

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?

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 aa | 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 af) = 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 ab) (Baz1 pq) = 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM