Haskell 理解Monad中的所有内容'&燃气轮机&燃气轮机=';功能?
以下是我在程序中实现的通用提升功能:Haskell 理解Monad中的所有内容'&燃气轮机&燃气轮机=';功能?,haskell,monads,forall,Haskell,Monads,Forall,以下是我在程序中实现的通用提升功能: 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成为任何类型([],可能等) 我现在正在研究monad中>=的定义: class Applicative m => Monad m w
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
成为任何类型([]
,可能
等)
我现在正在研究monad中>=
的定义:
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
很抱歉,我无法理解此
在函数定义中的作用?因为,与liftTuple
不同,它没有绑定到特定的函数(x->cx
)?基本上,当您不使用forall
时,所有类型在函数定义中都是全局的,这意味着它们都是在调用函数时推导出来的。使用forall
可以放弃函数的调用x
,直到函数本身被调用为止
因此,在第一个函数中,你有一个取x
并给出cx
的函数,然后你有一个a
和b
的元组,你期望一个ca
和cb
的元组。由于您已经说过第一个函数接受x
,因此可以使x
与a
相同,但它不会是b
,因为x
在整个声明中定义了一次。因此,您无法使函数同时接受a
和b
但是,在第二种情况下,x
范围仅限于执行x
的函数。我们基本上是说,有一个函数,它接受某个东西,并用该东西生成c
,它可以是任何类型。这使我们能够先将a
馈送到它,然后再将b
馈送到它,它就会工作<代码>x
现在在外部不一定是奇异的
您在Monad的定义中看到的是“ExplicitForAll”语言扩展。此扩展的上有一个描述
ExplicitForAll允许使用关键字'forall'显式声明类型在其自由类型变量中是多态的。它不允许写入任何无法写入的类型;它只允许程序员显式地声明(当前是隐式的)量化
这种语言扩展纯粹是可视化的,允许您显式地写出以前无法写出的变量。您只需从Monad
声明中省略所有ab.
,程序的功能将保持完全相同
比方说,通过这个扩展,您可以将
liftTupe
重写为所有abx的。(x->c x)->(a,b)->(c a,c b)
。定义是一样的,功能也是一样的,但读者现在会清楚地看到类型变量都是在最顶层定义的。Haskell类型系统中的所有类型变量都是通过一个来量化的。然而,在许多情况下,GHC可以推断量化,因此您不需要在源代码中编写它们
例如,liftTuple
和forall
的类型是显式的
liftTuple :: forall c a b. (forall x. x -> c x) -> (a, b) -> (c a, c b)
对于>>=
来说,情况是相同的 您编写的每个函数都在其类型变量上进行隐式通用量化:
id :: a -> a -- this is actually universally quantified over a
id :: forall a. a -> a
id x = x
实际上,您可以使用pragma语言启用此行为
此属性非常有用,因为它限制您编写仅适用于某些类型的代码。想想id
函数可以做什么:它可以返回参数,也可以永远循环。这是它唯一能做的两件事,您可以根据它的类型签名来确定这一点
强制多态函数的所有实例以相同的方式运行,而不管类型参数是什么,这被称为parametricity,Bartosz Milewski在博客文章中对此进行了解释。TL;DR is:使用参数化,我们可以保证out程序结构中的一些重新排序不会影响其行为。对于数学上更严格的处理,请参见Philip Wadler。单子定义中的所有内容只是为了使通用量化更明确。如果您的类型变量没有进一步的限制,那么默认情况下它是通用的,即可以是任何类型的变量
让我们看看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无法表达这一点,因此我们需要RANK2TYPE或RANKNTYPE。有了这些,我们可以写:
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)
不知道为什么所有的答案都这么冗长。用一句话来说:你是对的;它不会改变任何东西,只是为了显式地存在类型为>=
的code>绑定到一个特定的函数&functionma->(a->mb)->mb
。您也可以使用不带函数的for all
,例如[]::for all a。[a] ,空::用于所有f a。备选方案f=>f a
@Ryan,当我得到答案:“因为所有东西都是隐式限定的。”我的后续问题通常是“为什么?”。谢谢Malcolm,这是一个很好的答案。实际上,你不需要Monad
,来自Applicative
的纯就足够了。事实上,指向的
更弱。很好!applicative在语言标准中是monad的一个超类吗?是的,我相信是从GHC 7.8开始的。如果你想查找它,它被称为AMP或ApplicationMonad建议。