是否可以使用Data.Map
代替GADT
在Haskell中执行异构Dynamic
?我尝试按this answer中列出的异构集合进行建模:
{-# LANGUAGE GADTs #-}
class Contract a where
toString :: a -> String
data Encapsulated where
Encapsulate :: Contract a => a -> Encapsulated
getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = x
这个想法是Encapsulated
可以用来存储TypeClass a
的不同对象,然后在运行时提取特定类型。
我收到的x
类型与Contract a
不匹配的错误。也许我需要指定某种类约束来告诉GHC
x
中的Encapsulate x
类型与a
中的Contract 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对象,并且根据通过线路在运行时解码的类型,我们希望从{{1}检索适当的类型特定的builder
(在Map
中从配置文件加载到运行时IO,并传递给函数)并传递相同类型的解码JSON数据。
main
库可以在这里工作。但是,我有兴趣了解是否有其他可能的方法,例如Dynamic
或GADTs
。
答案 0 :(得分:4)
你的问题是你再次推出tr '\001' ' ' < inputfile
(这是行不通的) - 你可以做的就是在内部使用合同:
a
基本上编译器会告诉你需要知道的一切:你有一个useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x
(所以基本上是约束forall a. Contract a
是a
)
在Contract
上你没有这个约束 - 你告诉编译器:&#34;看起来这适用于每个getTypedObject :: Encapsulated -> a
我的需求&#34;
要实现这一目标,您必须将a
参数化为Encapsulated
,这显然是您不想要的。
第二个版本(我给的内部版本)有效,因为你对数据构造函数有约束,所以你可以在那里使用它
稍微扩大一点:
此
Encapsulated a
现在不会像getTypedObject :: Contract a => Encapsulated -> a
getTypedObject (Encapsulate x) = x
那样工作,但它仍然可以是两个不同类型的,它们只是共享这个类。
并且给编译器提示两者应该是相同的,你必须再次参数化Contract a
....
现在这样做:
Encapsulate
你删除了那些信息
答案 1 :(得分:4)
@Carsten答案显然是正确的,但我的两分钱让我明白了之前。
当你写:
getTypedObject :: Encapsulated -> a
你在说什么“是”:
getTypedObject
是一个可以取Encapsulated
类型值的函数,只要任何类型,就可以使用其结果。
你显然无法满足这一点,编译器不允许你尝试。您只能使用有关Encapsulated
内部值的知识,根据Contract
显示有意义的内容。换句话说,如果Contract
不在那里,那么你就无法用该值做任何有意义的事情。
这里的概念可以简单地描述为类型擦除,并且也存在于其他语言中,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
但很容易看出它会如何破裂,对吧? :)