简体   繁体   English

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

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

I've finished reading the Existential Types Wikibook , and it compares using forall with using lowercase letters for defining generic types.我已经阅读了 Existential Types Wikibook ,它比较了使用forall和使用小写字母来定义泛型类型。 It then says that the true usefulness of forall is when you use it with a type class.然后它说forall的真正用处在于当您将它与 class 类型一起使用时。 That is, forall make your function work with lots of types that adhere to some type class.也就是说, forall使您的 function 与许多遵循某些类型 class 的类型一起使用。

Example:例子:

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

Well, I found a real worl usage:好吧,我发现了一个真正的世界用法:

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

and you can see here, in the source that it uses forall but with no type class constraint:你可以在这里看到,在它使用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 ->
             {- ... -}

I'm very new to Haskell and trying to understand forall .我对 Haskell 很陌生,并试图理解forall

First, forget about existentials.首先,忘记存在主义。 They're a bit of a bodge – I personally never use that extension, only the strictly more general -XGADTs when needed.它们有点笨拙——我个人从不使用那个扩展,只有在需要时才使用严格更通用的-XGADTs
Also, allow me to use the symbol for universal quantification, which I find much more readable.另外,请允许我使用符号进行通用量化,我发现它更具可读性。 (Note that it looks a bit like the \ lambda, which is the value-level analogue of .) This requires -XUnicodeSyntax . (请注意,它看起来有点像\ lambda,它是的值级类似物。)这需要-XUnicodeSyntax

So, the signature所以,签名

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

is, for all outside purposes, exactly the same as就所有外部目的而言,与

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

or或者

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

When you see such a signature with explicit , the reason usually doesn't have anything to do with either -XExistentialQuantification or -XRankNTypes .当您看到这样一个带有显式的签名时,原因通常与-XExistentialQuantification-XRankNTypes没有任何关系。 Rather, they either simply found it clearer to explicitly say what are the type variables, or the definition may make use of -XScopedTypeVariables .相反,他们要么只是发现明确说明什么是类型变量更清楚,要么定义可以使用-XScopedTypeVariables For example, these two definitions are actually different:例如,这两个定义实际上是不同的:

{-# 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 doesn't compile, because both the global as well as the local signature is interpreted as implicitly quantified, ie as foo不编译,因为全局和本地签名都被解释为隐式量化,即

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

But that doesn't work, because now xAgain would have to have a polymorphic type independent of the type of x you passed in. By contrast, in foo' we only quantise once, and than the a from the global definition is also the type use in the local one.但这不起作用,因为现在xAgain必须具有独立于您传入的x类型的多态类型。相比之下,在foo'中我们只量化一次,并且来自全局定义的a也是类型在本地使用。

In the example of spock , they don't even use a scoped type variable, but I suspect they did during debugging and then just left the there.spock的示例中,他们甚至不使用作用域类型变量,但我怀疑他们在调试期间使用了,然后将留在那里。

A type declaration like类型声明,如

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

is implicitly universally quantified, ie means the same as是隐含的普遍量化的,即与

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

Both mean: f has a polymorphic type, where a and b can be anything (ie range over all types).两者都意味着: f具有多态类型,其中ab可以是任何东西(即范围涵盖所有类型)。 In this case the scope of the forall is the whole type expression, and it is usually left out, but it is always there, implicitly.在这种情况下, forall的 scope 是整个类型表达式,它通常被忽略,但它总是隐含地存在。 In your example, it is written explicitly, maybe to neatly enumerate the type variables.在您的示例中,它是明确编写的,可能是为了巧妙地枚举类型变量。

However, there are cases where you need the forall even without typeclasses: rank-N types , where the scope of the forall is not the whole type expression.但是,在某些情况下,即使没有类型类,您也需要forallrank-N types ,其中forall的 scope不是整个类型表达式。

A well-known example is used in the ST monad where you see the function: ST monad中使用了一个众所周知的示例,您可以在其中看到 function:

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

Note that the first forall can be omitted, as in the example above, but not the second.请注意,第一个forall可以省略,如上例所示,但不能省略第二个。 Note also that there are no typeclass constraints in the type of runST .另请注意, runST的类型中没有类型类约束。

One immediate utility is with TypeApplications extension, where it allows us to explicitly choose the ordering of type variables:一个直接的实用程序是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

I couldn't find it mentioned specifically at the link, but the above interaction was tested in repl.it.我在链接中找不到具体提到的,但是上面的交互是在repl.it中测试的。

Well, actually, the general description at the link does apply: b appears first in forall b a. a -> b -> b好吧,实际上,链接上的一般描述确实适用: b出现forall b a. a -> b -> b forall b a. a -> b -> b , when it is read from left to right. forall b a. a -> b -> b ,从左到右读取时。

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

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