如果不使用类型类约束,forall 有什么用?

Gat*_*ito 3 haskell forall

我已经读完了 Existential Types Wikibook,它比较forall了使用小写字母来定义泛型类型。然后它说真正的用处forall是当你将它与类型类一起使用时。也就是说,forall让您的函数可以使用许多符合某种类型类的类型。

例子:

 data ShowBox = forall s. Show s => SB s
Run Code Online (Sandbox Code Playgroud)

好吧,我发现了一个真正的世界用法:

spock :: forall conn sess st. SpockCfg conn sess st -> 
                               SpockM conn sess st () -> IO Middleware
<Source>
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到,在它使用但没有类型类约束的源代码forall

spock :: forall conn sess st. SpockCfg conn sess st -> 
                               SpockM conn sess st () -> IO Wai.Middleware
spock spockCfg spockAppl =
    do connectionPool <-
           case poolOrConn of
             PCNoDatabase ->
             {- ... -}
Run Code Online (Sandbox Code Playgroud)

我对 Haskell 很陌生,并试图理解forall.

lef*_*out 5

首先,忘记存在主义。它们有点麻烦——我个人从不使用该扩展名,只-XGADTs在需要时使用更通用的扩展名。
另外,请允许我使用?符号进行通用量化,我发现它更具可读性。(请注意,它看起来有点像\lambda,它是 的值级类似物?。)这需要-XUnicodeSyntax.

所以,签名

spock :: ? conn sess st. SpockCfg conn sess st -> SpockM conn sess st () -> IO Middleware
Run Code Online (Sandbox Code Playgroud)

对于所有外部目的,与

spock :: SpockCfg conn sess st -> SpockM conn sess st () -> IO Middleware
Run Code Online (Sandbox Code Playgroud)

或者

spock :: SpockCfg c s t -> SpockM c s t () -> IO Middleware
Run Code Online (Sandbox Code Playgroud)

当你看到这样一个带有明确的签名时?,原因通常与-XExistentialQuantification或没有任何关系-XRankNTypes。相反,他们要么只是发现明确说明什么是类型变量会更清楚,要么定义可能会使用-XScopedTypeVariables. 例如,这两个定义实际上是不同的:

{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}

foo :: a -> a
foo x = xAgain
 where xAgain :: a
       xAgain = x

foo' :: ? a . a -> a
foo' x = xAgain
 where xAgain :: a
       xAgain = x
Run Code Online (Sandbox Code Playgroud)

foo 不编译,因为全局和局部签名都被解释为隐式量化,即作为

foo :: ? a . a -> a
foo x = xAgain
 where xAgain :: ? ? . ?
       xAgain = x
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为现在xAgain必须有一个独立于x您传入的类型的多态类型。相比之下,foo'我们只量化一次,并且a来自全局定义的也是本地使用的类型.

在 的示例中spock,他们甚至不使用作用域类型变量,但我怀疑他们在调试期间使用了,然后就离开了?那里。