Haskell 中的异构 Data.Map
Heterogeneous Data.Map in Haskell
是否可以在 Haskell 中使用 GADT
而不是 Dynamic
来实现异构 Data.Map
?我尝试按照 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
不匹配的错误。也许我需要指定某种 class 约束来告诉 GHC
Encapsulate x
中的 x
类型与 Contract 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
(在 运行 时从配置文件中加载 main
的 IO,并传递给函数)并将其解码的 JSON 数据传递给它同一类型。
Dynamic
图书馆可以在这里工作。但是,我有兴趣了解是否还有其他可能的方法,例如 GADTs
或 datafamilies
.
你的问题是你再次将 a
推出(这将不起作用) - 你可以做的是在内部使用 合同 像这样:
useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x
基本上编译器会告诉你你需要知道的一切:在你的内部有一个forall a. Contract a
(所以基本上一个约束a
是一个Contract
)
在 getTypedObject :: Encapsulated -> a
你没有这个约束 - 你告诉编译器:"look this works for every a
I'll demand"
要实现它,您必须将 Encapsulated
参数化为 Encapsulated a
,这显然是您不想要的。
第二个版本(我给出的内部版本)有效,因为你对数据构造函数有约束,所以你可以在那里使用它
稍微扩展一下:
这个
getTypedObject :: Contract a => Encapsulated -> a
getTypedObject (Encapsulate x) = x
也不会工作,因为现在你会有 Contract a
,但它仍然可以是两种 不同类型 ,它们只是共享这个 class。
并向编译器提示两者应该相同,您必须再次参数化 Encapsulate
....
现在就这样做:
Encapsulate :: Contract a => a -> Encapsulated
您删除该信息
@Carsten 的回答显然是正确的,但我之前帮助我理解的两分钱。
当你写:
getTypedObject :: Encapsulated -> a
你是"saying"是:
getTypedObject
is a function that can take a value of the Encapsulated
type and its result can be used whenever any type whatsoever is needed.
你显然不能满足,而且编译器不允许你尝试。你只能利用关于Encapsulated
里面的值的知识,在Contract
的基础上带出一些有意义的东西。换句话说,如果 Contract
不存在,您将无法对该值做任何有意义的事情。
这里的概念可以简洁地描述为type erasure并且也存在于其他语言中,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
但是很容易看出它是如何崩溃的,对吧? :)
是否可以在 Haskell 中使用 GADT
而不是 Dynamic
来实现异构 Data.Map
?我尝试按照 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
不匹配的错误。也许我需要指定某种 class 约束来告诉 GHC
Encapsulate x
中的 x
类型与 Contract 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
(在 运行 时从配置文件中加载 main
的 IO,并传递给函数)并将其解码的 JSON 数据传递给它同一类型。
Dynamic
图书馆可以在这里工作。但是,我有兴趣了解是否还有其他可能的方法,例如 GADTs
或 datafamilies
.
你的问题是你再次将 a
推出(这将不起作用) - 你可以做的是在内部使用 合同 像这样:
useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x
基本上编译器会告诉你你需要知道的一切:在你的内部有一个forall a. Contract a
(所以基本上一个约束a
是一个Contract
)
在 getTypedObject :: Encapsulated -> a
你没有这个约束 - 你告诉编译器:"look this works for every a
I'll demand"
要实现它,您必须将 Encapsulated
参数化为 Encapsulated a
,这显然是您不想要的。
第二个版本(我给出的内部版本)有效,因为你对数据构造函数有约束,所以你可以在那里使用它
稍微扩展一下:
这个
getTypedObject :: Contract a => Encapsulated -> a
getTypedObject (Encapsulate x) = x
也不会工作,因为现在你会有 Contract a
,但它仍然可以是两种 不同类型 ,它们只是共享这个 class。
并向编译器提示两者应该相同,您必须再次参数化 Encapsulate
....
现在就这样做:
Encapsulate :: Contract a => a -> Encapsulated
您删除该信息
@Carsten 的回答显然是正确的,但我之前帮助我理解的两分钱。
当你写:
getTypedObject :: Encapsulated -> a
你是"saying"是:
getTypedObject
is a function that can take a value of theEncapsulated
type and its result can be used whenever any type whatsoever is needed.
你显然不能满足,而且编译器不允许你尝试。你只能利用关于Encapsulated
里面的值的知识,在Contract
的基础上带出一些有意义的东西。换句话说,如果 Contract
不存在,您将无法对该值做任何有意义的事情。
这里的概念可以简洁地描述为type erasure并且也存在于其他语言中,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
但是很容易看出它是如何崩溃的,对吧? :)