<p>Haskell对文件有一个等价的<code>with</code>,它被称为<code>withFile</code>。这个:</p>
<pre><code>with open("file1", "w") as f:
with open("file2", "r") as g:
k = g.readline()
f.write(k)
</code></pre>
<p>相当于:</p>
^{pr2}$
<p>现在,<code>withFile</code>可能看起来像是一元的东西。其类型为:</p>
<pre><code>withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
</code></pre>
<p>右边看起来像<code>(a -> m b) -> m b</code>。在</p>
<p>在cd6{cd6}中,你可以使用另一个<cd6},而不是cd6}。在</p>
<p>所以我要回答这个问题:<code>withFile</code>是一元的吗?在</p>
<p>你可以这样写:</p>
<pre><code>do f <- withFile "file1" WriteMode
g <- withFile "file2" ReadMode
k <- hGetLine g
hPutStr f k
</code></pre>
<p>但这不是类型检查。但它不能。在</p>
<p>这是因为在Haskell中IO monad是连续的</p>
<pre><code>do x <- a
y <- b
c
</code></pre>
<p>在执行<code>a</code>之后,<code>b</code>被执行,然后<code>c</code>。没有“回头路”
最后清理<code>a</code>或类似的东西。<code>withFile</code>另一方面,
必须在执行块后关闭句柄。在</p>
<p>还有另一个monad,叫做continuation monad,它允许这样做
东西。但是,现在有两个单子,IO和continuations,同时使用两个monad的效果需要使用monad转换器。在</p>
<pre><code>import System.IO
import Control.Monad.Cont
k :: ContT r IO ()
k = do f <- ContT $ withFile "file1" WriteMode
g <- ContT $ withFile "file2" ReadMode
lift $ hGetLine g >>= hPutStr f
main = runContT k return
</code></pre>
<p>太难看了。因此,答案是:有点,但这需要处理许多微妙的问题,使问题变得相当不透明。在</p>
<p>Python的<code>with</code>只能模拟monad所能做的有限的一部分——添加输入和终结代码。我不认为你能模拟</p>
<pre><code>do x <- [2,3,4]
y <- [0,1]
return (x+y)
</code></pre>
<p>使用<code>with</code>(这可能与一些肮脏的黑客)。相反,用于:</p>
<pre><code>for x in [2,3,4]:
for y in [0,1]:
print x+y
</code></pre>
<p>这里有一个Haskell函数-<code>forM</code>:</p>
<pre><code>forM [2,3,4] $ \x ->
forM [0,1] $ \y ->
print (x+y)
</code></pre>
<p>我重新编写了关于<code>yield</code>的阅读,它与单子的相似度比<code>with</code>更为相似:
<a href="http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html" rel="noreferrer">http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html</a></p>
<blockquote>
<p>A related question: if we have monads, do we need exceptions?</p>
</blockquote>
<p>基本上没有,你可以创建一个返回<code>Either A B</code>的函数,而不是抛出a或返回B的函数。<code>Either A</code>的monad的行为将与异常类似-如果一行代码将返回错误,则整个块将返回错误。在</p>
<p>但是,这意味着除法将使用<code>Integer -> Integer -> Either Error Integer</code>等类型来捕获被零除的操作。在使用除法的任何代码中,都必须检测出错误(显式模式匹配或使用绑定),甚至是最有可能出错的代码。Haskell使用异常来避免这样做。在</p>