<p>这几乎太微不足道了,但第一个问题是<code>with</code>不是函数,也没有将函数作为参数。您可以通过为<code>with</code>编写函数包装器轻松解决此问题:</p>
<pre><code>def withf(context, f):
with context as x:
f(x)
</code></pre>
<p>因为这太微不足道了,所以您不必费心区分<code>withf</code>和{<cd1>}。在</p>
<p><code>with</code>是monad的第二个问题是,作为语句而不是表达式,它没有值。如果可以给它一个类型,它将是<code>M a -> (a -> None) -> None</code>(这实际上是上面<code>withf</code>的类型)。实际上,您可以使用Python的<code>_</code>来获取<code>with</code>语句的值。在Python 3.1中:</p>
^{pr2}$
<p>由于<code>withf</code>使用的是函数而不是代码块,<code>_</code>的另一种方法是返回函数的值:</p>
<pre><code>def withf(context, f):
with context as x:
return f(x)
</code></pre>
<p>还有一件事阻止<code>with</code>(和<code>withf</code>)成为一元绑定。块的值必须是与<code>with</code>项具有相同类型构造函数的一元类型。实际上,<code>with</code>更通用。考虑到agf指出每个接口都是一个类型构造函数,我将<code>with</code>的类型标记为<code>M a -> (a -> b) -> b</code>,其中M是上下文管理器接口(<code>__enter__</code>和<code>__exit__</code>方法)。在<code>bind</code>和{<cd1>}类型之间是<code>M a -> (a -> N b) -> N b</code>。要成为monad,<code>with</code>必须在运行时失败,而<code>b</code>不是{<cd25>}。此外,虽然可以单独使用<code>with</code>作为绑定操作,但这样做几乎没有意义。在</p>
<p>您需要进行这些细微区分的原因是,如果您错误地认为<code>with</code>是一元的,那么您将错误地使用它并编写由于类型错误而失败的程序。换句话说,你会写垃圾。你需要做的是区分一个特定事物的结构(例如,一个单子)和一个可以以该事物的方式使用的结构(例如,一个单子)。后者要求程序员遵守纪律,或者定义额外的构造来执行规程。以下是<code>with</code>(类型是<code>M a -> (a -> b) -> M b</code>)的一个几乎一元的版本:</p>
<pre><code>def withm(context, f):
with context as x:
return type(context)(f(x))
</code></pre>
<p>在最后的分析中,您可以将<code>with</code>看作是一个组合子,但它比monads所要求的组合子(bind)更通用。使用monad的函数可能多于所需的两个函数(例如,list monad还具有cons、append和length),因此,如果您为上下文管理器定义了适当的绑定运算符(例如<code>withm</code>),那么{<cd1>}就涉及monad的意义而言可能是一元的。在</p>