了解Haskell的callCC示例 [英] Understanding Haskell callCC examples

查看:182
本文介绍了了解Haskell的callCC示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解以前的问题的答案。我希望对下面的解释能澄清事情。以下示例来自 fpcomplete

  import Control.Monad.Trans.Class 
import Control.Monad.Trans.Cont

main = flip runContT return $ do
lift $ putStrLnalpha
(k,num)< - callCC $ \ k - >>让f x = k(f,x)
作为回报(f,0)
lift $ putStrLnbeta
lift $ putStrLngamma
if num< 5
then k(num + 1)>> return()
else lift $ print num

输出是

  alpha 
beta
gamma
beta
gamma
beta
gamma
beta
gamma
beta
gamma
beta
gamma
5

我想我理解这个例子是如何工作的,但为什么在<$ c中有一个 let 表达式是必要的$ c> callCC 为返回延续,以便稍后使用。所以我试着直接返回下面的例子并修改它。

  import Control.Monad.Trans.Class 
import Control.Monad.Trans.Cont

main = flip runContT return $ do
lift $ putStrLnalpha
callCC $ \k - >做
k()
提升$ putStrLn呃哦...
提升$ putStrLnbeta
提升$ putStrLngamma
  alpha 
beta
gamma

我将它修改为以下内容:

  import Control.Monad.Trans.Class 
import Control.Monad.Trans.Cont

main = flip runContT return $ do
lift $ putStrLnalpha
f< - callCC $ \k - >做
提升$ putStrLn呃哦......
返回k
提升$ putStrLnbeta
提升$ putStrLngamma

这个想法是继续将返回为 f 并且在这个测试中未被使用我希望打印的例子

 呃哦... 
beta
gamma

但是这个例子不能编译,为什么不能这样做?



编辑:请考虑Scheme中的分析性例子。据我所知,Scheme不会有问题,这是否正确?,但为什么?。

解决方案

reverse 顺序中的示例。



最后一个示例不会因无限类型而受到类型检查。看看 callCC 的类型,它是((a - > ContT r m b) - > ContT r m a) - >控制。如果我们试图返回继续,我们返回类型为 ContT r m(a - > ContT r m b)的东西。这意味着我们得到类型等式约束 a〜(a - > ContT rmb),这意味着 a 必须是一个无限的类型。 Haskell不允许这些(一般来说,出于很好的理由 - 就我所知,这里的无限类型应该是一些东西,为它提供一个无限顺序函数作为参数)。

在第二个例子中,你没有提到你是否有什么困惑,但是。它不打印呃噢......的原因是因为 k()产生的 ContT ,与许多 ContT 操作不 使用以下计算不同。这是延续和返回 ContT 动作的普通函数之间的区别(免责声明,任何函数都可以返回一个 ContT 这样的行为,但一般)。所以,当你按照 k()打印或其他任何东西时,它是不相关的,因为 k()只是放弃以下操作。



所以,第一个例子。这里的let绑定实际上只是用来混淆 k 的参数。但通过这样做,我们避免了无限的类型。实际上,我们在与我们之前获得的无限类型相关的let绑定中做了一些递归。 f 有点像递归已经完成的延续版本。



这个lambda的类型我们传给 callCC Num n => ((n→ContT r m b,n)→ContT r m b)→> ContT r m(n - > ContT r m b,n)。这没有与你最后一个例子相同的无限类型问题,因为我们混淆了参数。您可以通过其他方式使用let绑定来执行类似的技巧,而无需添加额外的参数。例如:

  recur :: Monad m => ContT r m(ContT r m())
recur = callCC $ \k - >令r = k r in r>>返回r

这可能不是一个非常好解释的答案,但基本思想是返回直接延续会造成无限类型的问题。通过使用let绑定在lambda表达式中创建一些递归,并传递给 callCC ,您可以避免这种情况。


I am having trouble understanding the answers to a previous question. I'm hoping that an explanation of the following will clarify things. The following example comes from fpcomplete

import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont

main = flip runContT return $ do
    lift $ putStrLn "alpha"
    (k, num) <- callCC $ \k -> let f x = k (f, x)
                               in return (f, 0)
    lift $ putStrLn "beta"
    lift $ putStrLn "gamma"
    if num < 5
        then k (num + 1) >> return ()
        else lift $ print num

The output is

alpha
beta
gamma
beta
gamma
beta
gamma
beta
gamma
beta
gamma
beta
gamma
5

I think I understand how this example works, but why is it necessary to have a let expression in the callCC to "return" the continuation so that it can be used later on. So I tried to directly return the continuation by taking the following simpler example and modifying it.

import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont

main = flip runContT return $ do
    lift $ putStrLn "alpha"
    callCC $ \k -> do
      k ()
      lift $ putStrLn "uh oh..."
    lift $ putStrLn "beta"
    lift $ putStrLn "gamma"

This prints

alpha
beta
gamma

And I modified it to the following

import Control.Monad.Trans.Class
import Control.Monad.Trans.Cont

main = flip runContT return $ do
    lift $ putStrLn "alpha"
    f <- callCC $ \k -> do
      lift $ putStrLn "uh oh..."
      return k
    lift $ putStrLn "beta"
    lift $ putStrLn "gamma"

The idea being that the continuation would get returned as f and be unused in this test example which I would expect to print

uh oh...
beta
gamma

But this example doesn't compile, why can't this be done?

Edit: Consider the analgous example in Scheme. As far as I know Scheme wouldn't have a problem, is that correct?, but why?.

解决方案

Looking at your examples in reverse order.

The last example does not typecheck due to an infinite type. Looking at the type of callCC, it is ((a -> ContT r m b) -> ContT r m a) -> ContT r m a. If we try to return the continuation we return something of type ContT r m (a -> ContT r m b). This means we get the type equality constraint a ~ (a -> ContT r m b), which means a has to be an infinite type. Haskell does not allow these (in general, for good reason - as far as I can tell the infinite type here would be something along the lines of, supply it an infinite order function as an argument).

You don't mention whether there's anything you're confused about in the second example, but. The reason that it does not print "uh oh..." is because the ContT action produced by k (), unlike many ContT actions does not use the following computation. This is the difference between the continuations and just normal functions which return ContT actions (disclaimer, any function could return a ContT action like this, but in general). So, when you follow the k () up with a print, or anything else, it is irrelevant, because the k () just discards the following actions.

So, the first example. The let binding here is actually only used to mess around with the parameters to k. But by doing so we avoid an infinite type. Effectively, we do some recursion in the let binding which is related to the infinite type we got before. f is a little bit like a version of the continuation with the recursion already done.

The type of this lambda we pass to callCC is Num n => ((n -> ContT r m b, n) -> ContT r m b) -> ContT r m (n -> ContT r m b, n). This does not have the same infinite type problem that your last example has, because we messed around with the parameters. You can perform a similar trick without adding the extra parameter by using let bindings in other ways. For example:

recur :: Monad m => ContT r m (ContT r m ())
recur = callCC $ \k -> let r = k r in r >> return r

This probably wasn't a terribly well explained answer, but the basic idea is that returning the continuation directly will create an infinite type problem. By using a let binding to create some recursion inside the lambda you pass to callCC, you can avoid this.

这篇关于了解Haskell的callCC示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆