0

我已经定义了一个可以包含任何东西的通用数据类型(嗯,当前的实现不是完全任何东西)!

这是(完整代码):

{-#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]

我可以在控制台中使用它:

*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

好吧,我有它,但我需要以某种方式处理它。例如,让我们定义转换函数(函数定义,添加到前面的代码):

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

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

还有问题!之前代码中的fromAny定义不正确!而且我不知道如何使它正确。我在 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.

我也尝试了其他一些给出错误的方法。


我对此有相当糟糕的解决方案:通过showAnyT定义必要的函数并读取(替换以前的函数定义):

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

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

是的,这是工作。我可以玩它:

*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

但我认为这很糟糕,因为它使用字符串进行转换。

你能帮我找到整洁和好的解决方案吗?

4

3 回答 3

1

你所拥有的是一种叫做Existential type的东西。如果您点击该链接,您会发现在此模式中,使用容器类型内的“数据”的唯一方法是使用类型类。

在您当前的示例中,您提到a应该有ReadShow实例,这意味着只能使用这些类型类中的函数,而不能使用a其他任何东西,如果您想支持更多操作,a那么它应该受到所需类型类的约束。

像这样想:你可以把任何东西放在一个盒子里。现在,当你从那个盒子里取出一些东西时,你无法指定你会从中得到什么,因为你可以在里面放任何东西。现在如果你说你可以在这个盒子里放任何可以吃的东西,那么你肯定当你从这个盒子里挑选东西时它是可以吃的。

于 2013-11-20T08:06:31.100 回答
1

您正在使用 GADT 创建存在性数据类型。构造函数中的类型a存在,但无法恢复它。您唯一可用的信息是它具有ShowRead实例。确切的类型被忘记了,因为这是您的构造函数类型指示类型系统执行的操作。“确保这种类型有正确的实例,然后忘记它是什么。”

顺便说一句,您错过了一个功能:

readLike :: String -> AnyT -> AnyT
readLike s (Any a) = Any $ read s `asTypeOf` a

在模式匹配的上下文中,编译器知道无论有什么类型a,都有一个Read实例,它可以应用那个实例。尽管不确定是什么类型a。但它所能做的就是显示它,或者读取与它相同类型的字符串。

于 2013-11-20T08:08:12.843 回答
1

首先是免责声明:我不知道您要解决的问题的整个背景,但我得到的第一印象是这种对存在主义的使用是该工作的错误工具,您可能正在尝试实现一些在面向对象语言中很常见但不适合 Haskell 的代码模式。

就是说,像你这里的存在类型通常就像黑洞一样,一旦你把东西放进去,类型信息就会永远丢失,你不能将值转换回它的原始类型。Show但是,您可以通过类型类对存在值进行操作(就像您使用and所做的那样Read),因此您可以使用类型类Typeable来保留原始类型信息:

import Data.Typeable

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

现在你可以实现你拥有的所有功能,只要你给它们添加新的约束:

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可以实现为返回 a Maybe a(因为您无法确定您得到的值是否属于您期望的类型)。

fromAny :: Typeable a => AnyT -> Maybe a
fromAny (Any thing) = cast thing
于 2013-11-20T08:20:13.637 回答