繁体   English   中英

如果不使用类型 class 约束,forall 的用途是什么?

[英]What's the utility of forall if not using type class constraint?

我已经阅读了 Existential Types Wikibook ,它比较了使用forall和使用小写字母来定义泛型类型。 然后它说forall的真正用处在于当您将它与 class 类型一起使用时。 也就是说, forall使您的 function 与许多遵循某些类型 class 的类型一起使用。

例子:

 data ShowBox = forall s. Show s => SB s

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

spock :: forall conn sess st. SpockCfg conn sess st -> 
                               SpockM conn sess st () -> IO Middleware
<Source>

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

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 ->
             {- ... -}

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

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

所以,签名

spock :: ∀ conn sess st. SpockCfg conn sess st -> SpockM conn sess st () -> IO Middleware

就所有外部目的而言,与

spock :: SpockCfg conn sess st -> SpockM conn sess st () -> IO Middleware

或者

spock :: SpockCfg c s t -> SpockM c s t () -> IO Middleware

当您看到这样一个带有显式的签名时,原因通常与-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

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

foo :: ∀ a . a -> a
foo x = xAgain
 where xAgain :: ∀ α . α
       xAgain = x

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

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

类型声明,如

f :: A a -> B b -> C

是隐含的普遍量化的,即与

f :: forall a b . A a -> B b -> C

两者都意味着: f具有多态类型,其中ab可以是任何东西(即范围涵盖所有类型)。 在这种情况下, forall的 scope 是整个类型表达式,它通常被忽略,但它总是隐含地存在。 在您的示例中,它是明确编写的,可能是为了巧妙地枚举类型变量。

但是,在某些情况下,即使没有类型类,您也需要forallrank-N types ,其中forall的 scope不是整个类型表达式。

ST monad中使用了一个众所周知的示例,您可以在其中看到 function:

runST :: forall a. (forall s. ST s a) -> a

请注意,第一个forall可以省略,如上例所示,但不能省略第二个。 另请注意, runST的类型中没有类型类约束。

一个直接的实用程序是TypeApplications扩展,它允许我们显式选择类型变量的顺序:

 > foo :: forall a b. a -> b -> b ; foo x y = y
 > :t foo @ Int 1 2
foo @ Int 1 2 :: Num b => b    -- Int goes to the first argument

 > foo :: forall b a. a -> b -> b ; foo x y = y
 > :t foo @ Int 1 2
foo @ Int 1 2 :: Int           -- Int goes to the second argument

我在链接中找不到具体提到的,但是上面的交互是在repl.it中测试的。

好吧,实际上,链接上的一般描述确实适用: b出现forall b a. a -> b -> b forall b a. a -> b -> b ,从左到右读取时。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM