[英]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
具有多态类型,其中a
和b
可以是任何东西(即范围涵盖所有类型)。 在这种情况下, forall
的 scope 是整个类型表达式,它通常被忽略,但它总是隐含地存在。 在您的示例中,它是明确编写的,可能是为了巧妙地枚举类型变量。
但是,在某些情况下,即使没有类型类,您也需要forall
: rank-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.