Haskell显式forall,带“;“失踪”;lhs上的类型参数
我正在阅读的源代码,我偶然发现了下面的一段代码Haskell显式forall,带“;“失踪”;lhs上的类型参数,haskell,Haskell,我正在阅读的源代码,我偶然发现了下面的一段代码 data BlockedFetch r = forall a. BlockedFetch (r a) (ResultVar a) 并意识到我不完全理解ExplicitForall/存在量化的用法 上面的代码片段在哪些方面与 data BlockedFetch r a = BlockedFetch (r a) (ResultVar a) 为什么我可以“省略”数据声明lhs上的类型参数。不同之处在于,您可以(事实上,这种情况在代码中更进一步)创建一
data BlockedFetch r = forall a. BlockedFetch (r a) (ResultVar a)
并意识到我不完全理解ExplicitForall
/存在量化的用法
上面的代码片段在哪些方面与
data BlockedFetch r a = BlockedFetch (r a) (ResultVar a)
为什么我可以“省略”数据声明lhs上的类型参数。不同之处在于,您可以(事实上,这种情况在代码中更进一步)创建一个类型列表[BlockedFetch request]
,其中单个BlockedFetch
具有不同的a
类型(您无法使用[BlockedFetch请求a]
-此处的a
必须与整个列表相同。代码片段上方的注释很好地解释了这一点:
-- We often want to collect together multiple requests, but they return
-- different types, and the type system wouldn't let us put them
-- together in a list because all the elements of the list must have the
-- same type. So we wrap up these types inside the 'BlockedFetch' type,
-- so that they all look the same and we can put them in a list.
--
-- When we unpack the 'BlockedFetch' and get the request and the 'ResultVar'
-- out, the type system knows that the result type of the request
-- matches the type parameter of the 'ResultVar', so it will let us take the
-- result of the request and store it in the 'ResultVar'.
在Haxl中发生的大致情况是,您希望有一种从远程存储并行获取一组不同类型的值的方法。您这样做的方法是制作一组MVar
,其中包含您计划获取的值。然后,在您的代码中,您可以自由使用这些变量。但是,MVar
块直到它被“填满”
但是,要填充MVar
,只需要保留对MVar
的引用和填充方法-因此,在一天结束时,您甚至不需要知道MVar
将包含的内容的类型。这是一种存在类型-存在某种类型a
,而BlockedFetch
>将尝试填充,但会因不同的BlockedFetch
而有所不同。请考虑一个更简单的示例:
data ShowBox = forall a. Show a => ShowBox a
读作
data ShowBox = whatever a. Show a => ShowBox a
现在很明显,ShowBox
可以包含任何类型的值,只要该类型是Show
的实例
ex :: [ShowBox]
ex = [ShowBox 'a', ShowBox (), ShowBox [1,2,3]]
这样你就可以阅读了
data BlockedFetch r = forall a. BlockedFetch (r a) (ResultVar a)
由于“BlockedFetch
包含ra
和ResultVar a
,无论a
”如其他人所述,代码中的示例是存在量化的示例,因此与问题中的最后一个示例完全不同
首先要注意的是,现在GADT表示法通常是首选的。GADT表示法中的类型是:
数据阻塞取数器,其中
BlockedFetch::r a->ResultVar a->BlockedFetch r
我们可以显式绑定a
,如下所示:
数据阻塞取数器,其中
BlockedFetch::对于所有a.r a->ResultVar a->BlockedFetch r
如果我们有自由存在量化,这将同构于以下内容:
数据阻塞取数器,其中
BlockedFetch::(存在a.(RA,ResultVar a))->BlockedFetch r
这与不引入新关键字的愿望相结合,导致了旧的存在量化语法
BlockedFetch::for all r a.r a->ResultVar a->BlockedFetch r
这就是试图用语法传达的内容:
data BlockedFetch r=forall a.BlockedFetch(ra)(ResultVar a)
从这个角度来看,与使用blocked fetch r a
不同的是,数据构造函数类型的结果中会出现a
。示意图上,所有a.F a->G
在逻辑上等同于(存在a.F a)->G
,但显然不是所有a.F a->G a都等同于(存在a.F a)->G a
,因为后者甚至没有很好的范围
回到
BlockedFetch::for all r a.r a->ResultVar a->BlockedFetch r
如果你不理解存在论,但你确实理解普遍量化,那么你就可以理解这方面的情况。在这里,我们看到有人调用
BlockedFetch
,即使用数据构造函数构建值,可以自由选择他们想要的a
类型。有人消费,即patt在匹配上,类型为BlockedFetch R
的值实质上是为所有a.RA->ResultVar a->X编写一个函数,该函数必须适用于a
的任何值,即调用该函数的人可以选择a
,该函数必须使用该选项。这两种类型完全相同不同。例如,前者允许您拥有具有不同a
类型的BlockedFetch
值列表(即类型[BlockedFetch r]
有效且未提及a
),而后者则不能,因为类型将是[BlockedFetch r a]
但是现在所有元素的a
都必须匹配。关于GADT符号的观点与epsilonhalbe的问题高度相关。由存在量化
启用的所有
关键字的使用实际上与由RankNTypes
启用的关键字大不相同,后者是conf的潜在来源usion.GADT表示法使这一区别变得明显。+1
关于GADT语法,我想不出哪种情况下,存在量化与常规ADT语法相比看起来更好。