异构数据。Haskell中的映射
是否可以在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
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
但很容易看出这是怎么回事