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



我已经读完了存在类型维基教科书,它将使用forall与使用小写字母来定义泛型类型进行了比较。然后它说forall的真正有用性是当你把它与类型类一起使用时。也就是说,forall使函数适用于遵循某些类型类的许多类型。

例:

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

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

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

你可以在这里看到,在源代码中,它使用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 ->
{- ... -}

我对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的示例中,他们甚至不使用作用域类型变量,但我怀疑他们在调试期间这样做了,然后只是将留在那里。

一个直接的实用程序是带有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中,当它从左到右阅读时。

一个类型声明,如

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

隐含普遍量化,即意味着与

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

两者都意味着:f具有多态类型,其中ab可以是任何类型(即所有类型的范围)。在这种情况下,forall的作用域是整个类型表达式,它通常被省略,但它始终隐式存在。在您的示例中,它是显式编写的,可能是为了整齐地枚举类型变量。

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

STmonad 中使用了一个众所周知的示例,您可以在其中看到函数:

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

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

相关内容

  • 没有找到相关文章

最新更新