异构数据。Haskell中的映射

异构数据。Haskell中的映射,haskell,gadt,Haskell,Gadt,是否可以在Haskell中使用GADT而不是Dynamic进行异构数据映射?我尝试对异构集合进行建模,如下所示: 其思想是,封装的可用于存储类型的不同对象class a,然后在运行时提取特定类型的对象 我得到关于x类型不匹配的合同a的错误。也许我需要指定某种类型的类约束来告诉GHC在封装x中的x类型与合同a中的a相同 T.hs:10:34: Couldn't match expected type ‘a’ with actual type ‘a1’ ‘a1’ is a rig

是否可以在Haskell中使用
GADT
而不是
Dynamic
进行异构
数据映射?我尝试对异构集合进行建模,如下所示:

其思想是,封装的
可用于存储
类型的不同对象class a
,然后在运行时提取特定类型的对象

我得到关于
x
类型不匹配的
合同a
的错误。也许我需要指定某种类型的类约束来告诉
GHC
封装x
中的
x
类型与
合同a中的
a
相同

T.hs:10:34:
    Couldn't match expected type ‘a’ with actual type ‘a1’
      ‘a1’ is a rigid type variable bound by
           a pattern with constructor
             Encapsulate :: forall a. Contract a => a -> Encapsulated,
           in an equation for ‘getTypedObject’
           at T.hs:10:17
      ‘a’ is a rigid type variable bound by
          the type signature for getTypedObject :: Encapsulated -> a
          at T.hs:9:19
    Relevant bindings include
      x :: a1 (bound at T.hs:10:29)
      getTypedObject :: Encapsulated -> a (bound at T.hs:10:1)
    In the expression: x
    In an equation for ‘getTypedObject’:
        getTypedObject (Encapsulate x) = x
我之所以尝试这种方法,是因为我有不同类型的JSON对象,并且根据在运行时通过连线解码的类型,我们希望从
Map
中检索适当的特定于类型的
builder
(在运行时IO从配置文件
main
中加载,并传递到函数)并向其传递相同类型的解码JSON数据


Dynamic
库可以在这里工作。但是,我有兴趣了解是否还有其他可能的方法,例如
GADTs
datafamilies

您的问题是您再次将
a
推出(这将不起作用)-您可以在内部像这样使用合同:

useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x

基本上,编译器会告诉你你需要知道的一切:在里面你有一个
用于所有a。合同a
(因此基本上是一个约束
a
成为
合同

getTypedObject::enclosed->a
上,您没有这个约束-您正在告诉编译器:“看,这适用于我需要的每个
a

要实现它,您必须将
封装的
参数化为
封装的
,这显然是您不想要的

第二个版本(我给出的内部版本)之所以有效,是因为您对数据构造函数有约束,因此可以在那里使用它


在某种程度上说:

这个

也不会像现在那样工作,您将有
合同a
,但它可能是两种不同的类型,只是共享这个类

要向编译器提示两者应该相同,您必须再次参数化
封装

现在通过这样做:

Encapsulate :: Contract a => a -> Encapsulated

您删除了该信息

您的问题是您再次将
a
推出(这将不起作用)-您可以在内部像这样使用合同:

useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x

基本上,编译器会告诉你你需要知道的一切:在里面你有一个
用于所有a。合同a
(因此基本上是一个约束
a
成为
合同

getTypedObject::enclosed->a
上,您没有这个约束-您正在告诉编译器:“看,这适用于我需要的每个
a

要实现它,您必须将
封装的
参数化为
封装的
,这显然是您不想要的

第二个版本(我给出的内部版本)之所以有效,是因为您对数据构造函数有约束,因此可以在那里使用它


在某种程度上说:

这个

也不会像现在那样工作,您将有
合同a
,但它可能是两种不同的类型,只是共享这个类

要向编译器提示两者应该相同,您必须再次参数化
封装

现在通过这样做:

Encapsulate :: Contract a => a -> Encapsulated

你抹去了那些信息,卡斯滕的回答显然是对的,但我的两分钱帮助我理解了这一点

当你写作时:

getTypedObject :: Encapsulated -> a
你“说”的是:

getTypedObject
是一个函数,它可以接受
封装的
类型的值,其结果可以在需要任何类型时使用

您显然不能满足这一点,编译器也不允许您尝试。您只能使用有关
封装的
中的值的知识,根据
合同
得出一些有意义的东西。换句话说,如果没有
合同
,你就没有办法用这个价值做任何有意义的事情

这里的概念可以被描述为类型擦除,也存在于其他语言中,C++是我所知道的。因此,其价值在于删除关于类型的所有信息,除了您希望通过它们满足的契约保留的内容。缺点是恢复原始类型需要运行时检查


作为奖励,以下是动态方法的工作原理:

{-# LANGUAGE GADTs #-}

import Unsafe.Coerce

data Encapsulated where
   Encapsulate :: Show a => a -> Encapsulated

getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = unsafeCoerce x

printString :: String -> IO ()
printString = print

x = Encapsulate "xyz"
y = getTypedObject x

main = printString y

但很容易看出它是如何断裂的,对吗?:)

卡斯滕的回答显然是对的,但我的两分钱帮助我理解了这一点

当你写作时:

getTypedObject :: Encapsulated -> a
你“说”的是:

getTypedObject
是一个函数,它可以接受
封装的
类型的值,其结果可以在需要任何类型时使用

您显然不能满足这一点,编译器也不允许您尝试。您只能使用有关
封装的
中的值的知识,根据
合同
得出一些有意义的东西。换句话说,如果没有
合同
,你就没有办法用这个价值做任何有意义的事情

这里的概念可以被描述为类型擦除,也存在于其他语言中,C++是我所知道的。因此,其价值在于删除关于类型的所有信息,除了您希望通过它们满足的契约保留的内容。缺点是恢复原始类型需要运行时检查


作为奖励,以下是动态方法的工作原理:

{-# LANGUAGE GADTs #-}

import Unsafe.Coerce

data Encapsulated where
   Encapsulate :: Show a => a -> Encapsulated

getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = unsafeCoerce x

printString :: String -> IO ()
printString = print

x = Encapsulate "xyz"
y = getTypedObject x

main = printString y
但很容易看出这是怎么回事