在Monad>>> ='中理解forall功能?

时间:2017-02-05 08:58:04

标签: haskell monads forall

this回答后,我在我的计划中实施了通用提升功能:

liftTupe :: (x -> c x) -> (a, b) -> (c a, c b) --This will error
liftTuple :: (forall x. x -> c x) -> (a, b) -> (c a, c b)

我理解,在此上下文中,forall正在使x成为任何类型([]Maybe等。)。

我现在正在调查Monads中>>=的定义:

class Applicative m => Monad m where
   (>>=) :: forall a b. m a -> (a -> m b) -> m b

我无法理解此forall在函数定义中的作用?与liftTuple不同,它不受特定函数(x -> c x)的约束?

4 个答案:

答案 0 :(得分:6)

基本上,当你不使用forall时,所有类型在函数定义中都是全局的,这意味着它们都是在调用函数时推导出来的。使用forall,您可以放弃使用x的函数,直到它被调用为止。

所以在第一个你有一个函数,它需要x并给出c x,那么你有一个ab的元组,你期望一个元组与c ac b。由于您已经说第一个函数接受x,因此您可以使xa相同,但它不会b因为x因为a 1}}为整个声明定义一次。因此,您无法同时接受bx

但是,在第二种情况下,x范围仅限于采用c的函数。我们基本上说有一个函数需要一些东西,并使a用这个东西,它可以是任何类型。这使我们能够首先向其b提供x,然后Monad,这样就可以了。 forall a b.现在不必是单数的东西。

您在Monad定义中看到的是" ExplicitForAll"语言扩展。此扩展程序的Haskell Prime有说明

  

ExplicitForAll允许使用关键字' forall'明确声明类型在其自由类型变量中是多态的。它不允许写入任何尚未写入的类型;它只允许程序员明确说明(当前隐含的)量化。

这种语言扩展是纯视觉的,允许你明确地写出你以前无法做出的变量。您可以从liftTupe声明中省略forall a b x. (x -> c x) -> (a, b) -> (c a, c b),并且程序在功能上保持完全相同。

说,使用此扩展程序,您可以将O(1)重写为O(1)。定义是相同的,它的功能相同,但读者现在可以清楚地看到类型变量都是在最顶层定义的。

答案 1 :(得分:5)

您编写的每个函数都隐式地通过其类型变量进行量化:

id :: a -> a           -- this is actually universally quantified over a
id :: forall a. a -> a
id x = x

您实际上可以使用ExplicitForall语言编译指示启用此行为。

此属性非常有用,因为它限制您编写仅适用于某些类型的代码。想想id函数可以做什么:它可以返回它的参数或永远循环。这是它可以做的唯一两件事,你可以根据它的类型签名来解决这个问题。

强制所有多态函数实例的行为方式相同,不论类型参数如何称为 parametricity ,并在Bartosz Milewski的this博客文章中进行了解释。 TL; DR是:使用参数,我们可以保证程序结构中的一些重新排序不会影响它的行为。有关此问题的数学上更严格的处理,请参阅Philip Wadler撰写的Theorems for free!

答案 2 :(得分:3)

Haskell类型系统中的所有类型变量都由forall量化。但是,GHC可以在许多情况下推断量化,因此您不需要在源代码中编写它们。

例如,显式为liftTuple的{​​{1}}类型为

forall

对于liftTuple :: forall c a b. (forall x. x -> c x) -> (a, b) -> (c a, c b) ,情况是一样的。

答案 3 :(得分:2)

monad定义中的forall只是为了使通用量化更加明确。如果你有一个没有进一步限制的类型变量,它默认是通用的,即可以是任何东西。

让我们看看forall的两种用法之间的区别以及haskell如何看待它们:

隐式:

foo ::  (x -> f x) -> a -> b -> (f a, f b)
-- same as
foo :: forall f x a b . (x -> f x) -> a -> b -> (f a, f b)
-- our function is applied to a, so x is equal to a
foo :: forall f x a b . (x ~ a) => (x -> f x) -> a -> b -> (f a, f b)
-- our function is also applied to b, so x is equal to b
foo :: forall f x a b . (x ~ a, x ~ b) => (x -> f x) -> a -> b -> (f a, f b)

哦,(x~a,x~b)需要(a~b)。这将在没有注释的情况下推断,但由于我们明确使用了不同的类型变量,所以一切都会爆炸。为了解决这个问题,我们需要f在我们的函数中保持多态。

标准的haskell无法表达这一点,因此我们需要rank2types或rankntypes。有了这个,我们可以写:

foo ::  (forall x . x -> f x) -> a -> b -> (f a, f b)

请注意,forall是函数类型的一部分。这样它在我们的功能中保持多态性,我们可以将它应用于不同的类型,而不会爆炸!

请注意,我们也可以这样做:

foo :: Monad m => a -> b -> (m a, m b)
foo a b = (return a, return b)