Data.Dynamic
有以下实现:
data Dynamic where
Dynamic :: TypeRep a -> a -> Dynamic
字符串
我认为下面的定义是等价的(至少我认为是这样):
data Dynamic where
Dynamic :: Typeable a => a -> Dynamic
型
因为可以使用withTypeable
从TypeRep a
得到Typeable
约束,而在另一个方向上,可以使用typeRep
从Typeable
约束得到TypeRep a
。
我问这个问题是因为我经常使用类型类约束创建GADT,通常是为了创建“存在”类型,看到这种类型使我怀疑是否应该添加一个“见证”字段,而不是使用类型类约束?在这两种方法之间进行选择时,我应该考虑什么?
进一步的想法...
考虑一下这样的事情:
data SillyListA m where
SillyListA :: Ord a => (a -> m ()) -> [a] -> SillyList m
data SillyListB m where
SillyListB :: (a -> a -> Ordering) -> (a -> m ()) -> [a] -> SillyList m
型
这里明确参数而不是仅仅包含类型类约束有一个实际的目的,因为你可以在同一类型上有不同的顺序,第二个定义允许这样做,而不会愚蠢地使用newtype。
但是,如果你的类型基本上是一个单例,就像TypeRep a
中的情况一样,这似乎很愚蠢。
我想一个小小的好处是,也许见证可以成为函数的参数,这意味着你不必使用类型应用程序来提取构造函数。
就像我能做的第一个定义:
f (Dynamic tr x) = ...
型
而不是
f (Dynamic @a x) = ...
型
但我发现我做的是:
f (Dynamic @a _ x) = ...
型
因为如果f
定义了任何显式类型的子函数,并且没有多少函数将TypeRep a
作为参数,则该类型在作用域中非常方便,它们通常需要类型应用程序或采用Proxy @a
,所以无论如何我最终都需要在作用域中使用该类型。
我已经开始考虑这个问题,因为我已经在自己的代码中定义了类型(如果我重新发明了轮子,并且在其他地方存在,请大声喊出来):
data DynamicF f where
DynamicF :: forall (a :: Type) f. TypeRep a -> f a -> DynamicF f
型
我把它定义成这样,基本上是复制Dynamic
,但我想也许只是:
data DynamicF f where
DynamicF :: forall (a :: Type). Typeable a => f a -> DynamicF f
型
是一个更好的定义。
1条答案
按热度按时间gdrx4gfi1#
我怀疑答案大多只是历史。
Dynamic
比ExistentialQuantification
古老得多。(GHC 6.8.1,根据手册),这是必要的两个现代定义。在此扩展之前,你不能在数据类型中存储像a
这样的类型,也不能存储像Typeable a
这样的约束。(即使它没有提到像a
这样的存储类型)。例如,在GHC 5.04源代码(即使作为bzip存档,也小于5 MB!)中,我发现
Dynamic
的定义如下字符串
我认为这是合理的建议,部分原因是
Dynamic
现在包含一个TypeRep
,因为它 * 总是 * 持有一个TypeRep
。由于
Dynamic
数据构造函数过去是不可导出的,因此库作者 * 可以 * 在base-4.10
/GHC 8.2.1中公开它时将其更改为使用Typeable
,而不会破坏用户代码。但没有强烈的理由这样做。实际上,甚至有两个弱理由更喜欢TypeRep
版本:TypeRep a
中获取Typeable a
实际上是一种黑魔法,(本质上)是通过unsafeCoerce
将Typeable a => r
转化为TypeRep a -> r
来实现的。(当然,它仍然是 * 安全的 *,在最近的GHC中,这种技巧显然已经被编入了自己的withDict
原语中。)TypeRep a
s是“正常”值,涉及较少的黑魔法。TypeRep a
是一个值,你可以用一个名称绑定它,它可以用作Proxy
-esque标记来表示类型a
。(注意,许多API不需要Proxy a
,而是需要proxy a
,其中proxy
被量化。)Typeable a
在您得到它的那一刻就消失在背景中。这对于Dynamic
来说有点无意义,因为你也得到了一个实际的值x :: a
(所以你可以这样做,例如let proxyOf :: a -> Proxy a in proxyOf x
),但是已经给出了一个TypeRep
是很好的。还要注意,在Data.Dynamic
公开其构造函数的时候,类型应用模式还不存在。这两点在当时基本上都只是表面上的考虑,在今天甚至不那么重要。
现在,对于今天的代码编写,我将指出类型应用程序/抽象仍然不能做代理可以做的所有事情。将类型参数命名为代理的能力仍然缺失,因此,出于技术原因,您必须选择代理式API而不是类型应用程序API。
型
如果你没有预料到这种情况会发生在你身上,或者你只是不介意你的代码中的一个小的不一致,在这里
withSomeStructure'
给你一个Proxy
,但是假设的withDynamicF
没有,那么DynamicF
的Typeable
版本就可以了。我个人还是更喜欢TypeRep
版本。