Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell中的Goto:有人能解释继续使用monad的这种看似疯狂的效果吗?_Haskell_Continuations_Monad Transformers - Fatal编程技术网

Haskell中的Goto:有人能解释继续使用monad的这种看似疯狂的效果吗?

Haskell中的Goto:有人能解释继续使用monad的这种看似疯狂的效果吗?,haskell,continuations,monad-transformers,Haskell,Continuations,Monad Transformers,Tomasz Zielonka从thread(Control.Monad.Cont-fun,2005)中引入了一个函数(由Thomas Jäger以清晰而友好的方式进行了评论)。Tomasz接受callCC body的参数(函数),并使用以下两个定义返回该参数供以后使用: import Control.Monad.Cont ... getCC :: MonadCont m => m (m a) getCC = callCC (\c -> let x = c x in return x

Tomasz Zielonka从thread(Control.Monad.Cont-fun,2005)中引入了一个函数(由Thomas Jäger以清晰而友好的方式进行了评论)。Tomasz接受callCC body的参数(函数),并使用以下两个定义返回该参数供以后使用:

import Control.Monad.Cont
...
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)

getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
这些也在本文中提到。使用它们,您可以像haskell中的goto语义一样,看起来非常酷:

import Control.Monad.Cont

getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))

main :: IO ()
main = (`runContT` return) $ do
    (x, loopBack) <- getCC' 0
    lift (print x)
    when (x < 10) (loopBack (x + 1))
    lift (putStrLn "finish")
数字0到3在这个例子的黑暗中被吞没了

现在,在“真实世界哈斯克尔”奥沙利文,戈尔岑和斯图尔特州

“叠加单变量变换器类似于组合函数。如果我们改变应用函数的顺序,然后得到不同的结果,我们不会感到惊讶。单变量变换器也是如此。”(现实世界Haskell,2008,第442页)

我想出了交换上面变压器的主意:

--replace in the above example
type APP= ContT () (WriterT [String] IO)
...
runAPP a = do
    (_,w) <- runWriterT $ runContT a (return . const ())
    putStrLn $ unlines w
添加这些行,编译并运行。所有数字都打印出来了


上一个例子中发生了什么?

我花了一些时间在λ演算中跟踪这一点。它生成了一页又一页的派生,我不会在这里重复,但我确实对monad堆栈的工作原理有了一些了解。您的类型扩展如下:

type APP a = WriterT [String] (ContT () IO) a
           = ContT () IO (a,[String])
           = ((a,[String]) -> IO()) -> IO()
您可以类似地扩展Writer的
返回
>=
告诉
,以及Cont的
返回
=
调用
。然而,追踪它是极其乏味的

在驱动程序中调用
循环
的效果是放弃正常的继续,而是再次从调用返回到
getCC'
。该放弃的延续包含将当前
x
添加到列表中的代码。因此,我们重复循环,但是现在
x
是下一个数字,并且只有当我们到达最后一个数字时(因此不要放弃继续),我们才拼凑出
[“快速棕色狐狸”]
[“4”]
的列表


正如“真实世界的哈斯克尔”强调IO单子需要保持在堆栈的底部一样,继续单子保持在顶部似乎也很重要。

这里有一个非正式的答案,但希望有用
getCC'
返回当前执行点的延续;您可以将其视为保存堆栈帧。由
getCC'
返回的continuation不仅在调用点具有
ContT
的状态,而且还具有堆栈上
ContT
上方的任何monad的状态。当您通过调用continuation来恢复该状态时,在
ContT
上构建的所有monad都会在
getCC'
调用点返回到它们的状态

在第一个示例中,您使用
type APP=WriterT[String](ContT()IO)
,将
IO
作为基本单子,然后使用
ContT
,最后使用
WriterT
。因此,当您调用
loop
时,写入程序的状态将恢复到调用
getCC'
时的状态,因为写入程序位于monad堆栈上的
ContT
上方。当您切换
ContT
WriterT
时,现在continuation只会解开
ContT
单子,因为它比writer高

ContT
并不是唯一一个会导致类似问题的monad转换器。下面是一个类似情况的示例,
error

func :: Int -> WriterT [String] (ErrorT String IO) Int
func x = do
  liftIO $ print "start loop"
  tell [show x]
  if x < 4 then func (x+1)
    else throwError "aborted..."

*Main> runErrorT $ runWriterT $ func 0
"start loop"
"start loop"
"start loop"
"start loop"
"start loop"
Left "aborted..."
这里保存了writer monad的内部状态,因为它低于monad堆栈上的
error
error
ContT
之间的最大区别在于
error
的类型清楚地表明,如果抛出错误,任何部分计算都将被丢弃


当单子位于堆栈顶部时,对其进行推理肯定更简单,但有时能够将单子展开到已知点是有用的。例如,一种类型的事务可以通过这种方式实现。

两种答案似乎都得出结论,控制流monad转换器,如
ContT
error
应该位于monad堆栈的顶部,类似于
IO
应该位于底部。我对单子知之甚少,对
ContT
error
知之甚少,但这似乎对我来说是真的。这可能是写学术论文、书籍或其他东西的好材料。@Dan,
error
通常放在底部,就在
IO
的正上方。这是因为,如果计算中有错误,则来自其他monad(尤其是state或writer)的任何值都是局部的,通常不提供信息。尽管如此,你的建议还是有点令人信服,我也希望在这方面看到更多。帖子中的链接已经死了,但是信息可以在谢谢你,约翰!这确实促进了我对延续单子的理解。至少,彻底地重新思考一下,它在某种程度上也是显而易见的。关于事务的想法在这里很有说服力。你说的λ演算是什么?听起来很有趣,它是函数式编程的一种数学吗?谢谢你的提示!这是函数编程的基础,通过数学逻辑。Haskell中的函数literal:(\x->x+1)基本上是一个lambda表达式,由λx.x+1编写。将其应用于参数6,用6代替主体中的x:(λx.x+1)6↪ 6+1 ↪ 7.这种取代称为β还原。
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
  tell = lift . tell
  listen = undefined
  pass = undefined
type APP a = WriterT [String] (ContT () IO) a
           = ContT () IO (a,[String])
           = ((a,[String]) -> IO()) -> IO()
func :: Int -> WriterT [String] (ErrorT String IO) Int
func x = do
  liftIO $ print "start loop"
  tell [show x]
  if x < 4 then func (x+1)
    else throwError "aborted..."

*Main> runErrorT $ runWriterT $ func 0
"start loop"
"start loop"
"start loop"
"start loop"
"start loop"
Left "aborted..."
switch :: Int -> ErrorT String (WriterT [String] IO) () 
switch x = do
  liftIO $ print "start loop"
  tell [show x]
  if x < 4 then switch (x+1)
    else throwError "aborted..."

*Main> runWriterT $ runErrorT $ switch 0
"start loop"
"start loop"
"start loop"
"start loop"
"start loop"
(Left "aborted...",["0","1","2","3","4"])