Search code examples
haskellghcgadt

Can't properly define transformation from universal type, that defined with GADT


I've defined an universal data type that can contain anything (well, with current implementation not totally anything)!

Here it is (complete code):

{-#LANGUAGE NoMonomorphismRestriction#-}
{-#LANGUAGE GADTs#-}
{-#LANGUAGE StandaloneDeriving#-}

data AnyT where
    Any :: (Show a, Read a) => a -> AnyT

readAnyT :: (Read a, Show a) => (String -> a) -> String -> AnyT
readAnyT readFun str = Any $ readFun str

showAnyT :: AnyT -> String
showAnyT (Any thing) = show thing

deriving instance Show AnyT --Just for convinience!

a = [Any "Hahaha", Any 123]

And I can play with it in console:

*Main> a
[Any "Hahaha",Any 123]
it :: [AnyT]
*Main> readAnyT (read::String->Float) "134"
Any 134.0
it :: AnyT
*Main> showAnyT $ Any 125
"125"
it :: String

Well, I have it, but I need to process it somehow. For example, let's define transformation functions (functions definition, add to previous code):

toAnyT :: (Show a, Read a) => a -> AnyT -- Rather useless
toAnyT a = Any a

fromAny :: AnyT -> a
fromAny (Any thing) = thing

And there is the problem! the fromAny definition from previous code is incorrect! And I don't know how to make it correct. I get the error in GHCi:

2.hs:18:23:
    Could not deduce (a ~ a1)
    from the context (Show a1, Read a1)
      bound by a pattern with constructor
                 Any :: forall a. (Show a, Read a) => a -> AnyT,
               in an equation for `fromAny'
      at 2.hs:18:10-18
      `a' is a rigid type variable bound by
          the type signature for fromAny :: AnyT -> a at 2.hs:17:12
      `a1' is a rigid type variable bound by
           a pattern with constructor
             Any :: forall a. (Show a, Read a) => a -> AnyT,
           in an equation for `fromAny'
           at 2.hs:18:10
    In the expression: thing
    In an equation for `fromAny': fromAny (Any thing) = thing
Failed, modules loaded: none.

I tried some other ways that giving errors too.


I have rather bad solution for this: defining necessary functions via showAnyT and read (replace previous function definitions):

toAnyT :: (Show a, Read a) => a -> AnyT -- Rather useless
toAnyT a = Any a

fromAny :: Read a => AnyT -> a
fromAny thing = read (showAnyT thing)

Yes, it's work. I can play with it:

*Main> fromAny $ Any 1352 ::Float
1352.0
it :: Float
*Main> fromAny $ Any 1352 ::Int
1352
it :: Int
*Main> fromAny $ Any "Haha" ::String
"Haha"
it :: String

But I think it's bad, because it uses string to transform.

Could you please help me to find neat and good solution?


Solution

  • First a disclaimer: I don't know the whole context of the problem you are trying to solve, but the first impression I get is that this kind of use of existentials is the wrong tool for the job and you might be trying to implement some code pattern that is common in object-oriented languaged but a poor fit for Haskell.

    That said, existential types like the one you have here are usually like black holes where once you put something in, the type information is lost forever and you can't cast the value back to its original type. However, you can operate on existential values via typeclasses (as you've done with Show and Read) so you can use the typeclass Typeable to retain the original type information:

    import Data.Typeable
    
    data AnyT where
        Any :: (Show a, Read a, Typeable a) => a -> AnyT
    

    Now you can implement all the functions you have, as long as you add the new constraint to them as well:

    readAnyT :: (Read a, Show a, Typeable a) => (String -> a) -> String -> AnyT
    readAnyT readFun str = Any $ readFun str
    
    showAnyT :: AnyT -> String
    showAnyT (Any thing) = show thing
    
    toAnyT :: (Show a, Read a, Typeable a) => a -> AnyT -- Rather useless
    toAnyT a = Any a
    

    fromAny can be implemented as returning a Maybe a (since you cannot be sure if the value you are getting out is of the type you are expecting).

    fromAny :: Typeable a => AnyT -> Maybe a
    fromAny (Any thing) = cast thing