标签: continuations

是否可以以编程方式构造Python堆栈帧并在代码中的任意点开始执行?

是否可以在CPython中以编程方式构造堆栈(一个或多个堆栈帧)并在任意代码点开始执行?想象一下以下场景:

  1. 您有一个工作流引擎,其中工作流可以在Python中编写脚本,其中包含一些构造(例如分支,等待/加入),这些构造是对工作流引擎的调用.

  2. 阻塞调用(例如等待或连接)在具有某种持久性后备存储的事件调度引擎中设置侦听器条件.

  3. 您有一个工作流脚本,它调用引擎中的等待条件,等待稍后将发出信号的某些条件.这将在事件调度引擎中设置侦听器.

  4. 工作流脚本的状态,包括程序计数器(或等效状态)的相关堆栈帧是持久的 - 因为等待条件可能在几天或几个月后发生.

  5. 在此期间,工作流引擎可能会停止并重新启动,这意味着必须能够以编程方式存储和重新构建工作流脚本的上下文.

  6. 事件调度引擎触发等待条件获取的事件.

  7. 工作流引擎读取序列化状态并堆栈并使用堆栈重构线程.然后在调用等待服务的点继续执行.

问题

可以使用未修改的Python解释器吗?更好的是,有人能指出一些可能涵盖此类事物的文档,或者以编程方式构造堆栈帧并在代码块中间某处开始执行的代码示例吗?

编辑:为了澄清'未修改的python解释器',我不介意使用C API(在PyThreadState中是否有足够的信息来执行此操作?)但我不想去探讨Python解释器的内部并且有建立一个修改过的.

更新:从一些初步调查,可以获得执行上下文PyThreadState_Get().这将返回PyThreadState(定义在pystate.h)中的线程状态,该状态具有对堆栈帧的引用frame.堆栈帧保存在一个结构类型中PyFrameObject,定义为frameobject.h. PyFrameObject有一个字段f_lasti(bobince的道具),它有一个程序计数器,表示为从代码块开头的偏移量.

这最后是一个好消息,因为它意味着只要您保留实际编译的代码块,您就应该能够根据需要为尽可能多的堆栈帧重建本地,并重新启动代码.我想说这意味着它在理论上是可能的,而不必制作一个修改过的python interpereter,虽然这意味着代码仍然可能会非常繁琐并与特定版本的解释器紧密耦合.

剩下的三个问题是:

  • 事务状态和'saga'回滚,这可能是通过一种用于构建O/R映射器的元类黑客来完成的.我确实曾经构建过一次原型,因此我对如何实现这一目标有了一个很好的了解.

  • 强大地序列化事务状态和任意本地.这可以通过读取__locals__(可从堆栈帧获得)并以编程方式构建对pickle的调用来完成.但是,我不知道在那里可能有什么,如果有的话.

  • 版本控制和升级工作流程.这有点棘手,因为系统没有为工作流节点提供任何符号锚.我们所拥有的只是锚点为了做到这一点,人们必须确定所有入口点的偏移并将它们映射到新版本.可能手动完成,但我怀疑自动化很难.如果您想支持此功能,这可能是最大的障碍.

更新2: ( PyCodeObject)code.h有地址的列表(f_lasti) - >在行号的映射PyCodeObject.co_lnotab(如果错在这里指正).这可以用于促进迁移过程以将工作流更新到新版本,因为冻结的指令指针可以映射到新脚本中的适当位置,根据行号完成.仍然相当混乱,但更有希望.

更新3:我认为答案可能是Stackless Python. 您可以暂停任务并将其序列化.我还没有弄清楚这是否也适用于堆栈.

python continuations stack serialization python-stackless

22
推荐指数
3
解决办法
2400
查看次数

Haskell中的精确流量控制

想法

你好!我正在尝试在Haskell中实现一个基于数据流意识形态的图像处理库.我有一个问题与我想要如何处理控制流程有关.

主要想法是介绍一个time.的timeFloat,它可以在任何地方的代码访问(你可以认为它像约州单子,但有点滑稽).有趣的是,我们可以timeShift对结果使用操作,影响相应操作的时间.

一个例子最好解释这种情况.让我们使用以下数据流图:

--               timeShift(*2) --
--              /                 \
-- readImage --                    addImages -> out
--              \                 /
--                blur ----------
Run Code Online (Sandbox Code Playgroud)

和它的伪代码(它不是类似的 - 如果我们在这里使用或者记号,它不重要,这个想法应该是清楚的):

test = do
    f      <- frame
    a      <- readImage $ "test" + show f + ".jpg"
    aBlur  <- blur a
    a'     <- a.timeShift(*2)
    out    <- addImage aBlur a'

main = print =<< runStateT test 5
Run Code Online (Sandbox Code Playgroud)

5time我们想要运行的test功能.该timeShift函数影响其左侧的所有操作(在数据流图中) - …

algorithm monads continuations haskell functional-programming

22
推荐指数
1
解决办法
797
查看次数

寻找延续的"真实"用法的例子

我正在努力掌握延续的概念,我在维基百科的文章中找到了几个像这样的小教学例子:

(define the-continuation #f)

(define (test)
  (let ((i 0))
    ; call/cc calls its first function argument, passing 
    ; a continuation variable representing this point in
    ; the program as the argument to that function. 
    ;
    ; In this case, the function argument assigns that
    ; continuation to the variable the-continuation. 
    ;
    (call/cc (lambda (k) (set! the-continuation k)))
    ;
    ; The next time the-continuation is called, we start here.
    (set! i (+ i 1))
    i))
Run Code Online (Sandbox Code Playgroud)

我理解这个小函数的作用,但是我看不出任何明显的应用.虽然我不希望很快在我的代码中使用延续,但我希望我知道一些它们可能适合的情况.

所以我正在寻找更多有用的代码示例,这些代码示例可以为程序员提供什么延续.

干杯!

ruby language-agnostic scheme continuations smalltalk

21
推荐指数
6
解决办法
2787
查看次数

库里 - 霍华德是双重否定的通讯员((a-> r) - > r)还是((a->⊥) - >⊥)?

这是库里 - 霍华德的双重否定的记者a; (a -> r) -> r或者(a -> ?) -> ?,或两者兼而有之?

两种类型都可以在Haskell中编码如下,其中?编码为forall b. b.

p1 :: forall r. ((a -> r) -> r)
p2 :: (a -> (forall b. b)) -> (forall b. b)
Run Code Online (Sandbox Code Playgroud)

Wadler 2003的论文以及 Haskell中的实现似乎采用了前者,而其他一些文献(例如本文)似乎支持后者.

我目前的理解是后者是正确的.我理解前者的风格,因为你可以创建类型的值的困难a,从forall r. ((a -> r) -> r)使用纯计算:

> let p1 = ($42) :: forall r. (Int -> r) -> r
> p1 id
42
Run Code Online (Sandbox Code Playgroud)

这似乎与你不能获得直觉逻辑相矛盾 …

continuations haskell curry-howard

21
推荐指数
2
解决办法
625
查看次数

Python和Ruby中Web编程的协程或延续.为什么不?

问题是:为什么不继续(在Ruby中)和coroutines(在Python中)更广泛地用于Web编程?

由于在请求之间保持状态的问题使得服务器端web编程变得困难.两个优雅且相关的解决方案是继续(如Scheme,Ruby和Smalltalk中所见)和协同程序(如Python和Go中所见).

Bruce Tate在他的Beyond Java(O'Reilly,2005)中谈到Ruby on Rails和Seaside是Web编程中令人兴奋的新发展.对我而言,Seaside是真正的突破:使用Smalltalk延续,Seaside使编写复杂的请求/响应序列变得非常容易.

我知道在Tornado和Twisted中使用Python协程来减少回调,但与在单个协程中编写视图/控制器来处理一系列HTTP请求以完成在线购买相比,这有点低级别.

我想知道为什么这些看起来如此优秀的想法在Python和Ruby中没有得到更广泛的部署.这只是一种文化问题吗?以这些语言实施的设施限制?或者,在Web开发的背景下,这些方法存在根本缺陷吗?

ruby python continuations go coroutine

20
推荐指数
1
解决办法
1101
查看次数

为什么Common Lisp中不存在原始的`call-with-current-continuations`?

最近,我一直在调查Scheme和Common Lisp之间关于这两种语言对延续的方法的区别.

我注意到Common Lisp方法比Scheme方法更保守.

此外,Scheme提供了一个原始的call-with-current-continuation,通常是缩写的call/cc,它在ANSI Common Lisp规范中没有等价物(尽管有一些库试图实现它们).

有没有人知道为什么决定不在ANSI Common Lisp规范中创建类似的原语?

提前致谢.

lisp scheme continuations callcc common-lisp

20
推荐指数
3
解决办法
3091
查看次数

使用Cont从未来和过去获取值

我正在Haskell写一个brainfuck解释器,我想出了一个我认为对程序非常有趣的描述:

data Program m = Instruction (m ()) (Program m)
               | Control (m (Program m))
               | Halt
Run Code Online (Sandbox Code Playgroud)

但是,将brainfuck程序的文本表示解析为此数据类型是很棘手的.尝试正确解析方括号时会出现问题,因为有一些结点可以使Instruction循环中的最终内容Control再次链接到循环.

更多初步信息.有关所有详细信息,请参阅github repo上的此版本.

type TapeM = StateT Tape IO
type TapeP = Program TapeM
type TapeC = Cont TapeP

branch :: Monad m => m Bool -> Program m -> Program m -> Program m
branch cond trueBranch falseBranch =
  Control ((\b -> if b then trueBranch else falseBranch) `liftM` cond)

loopControl :: TapeP -> TapeP -> …
Run Code Online (Sandbox Code Playgroud)

monads continuations haskell monadfix tying-the-knot

19
推荐指数
1
解决办法
1441
查看次数

Kiselyov拉链的惯用斯卡拉翻译?

Oleg Kiselyov 展示了如何通过使用分隔的延续从任何可穿越的拉链制作拉链.他的Haskell代码非常简短:

module ZipperTraversable where 

import qualified Data.Traversable as T
import qualified Data.Map as M


-- In the variant Z a k, a is the current, focused value
-- evaluate (k Nothing) to move forward
-- evaluate (k v)       to replace the current value with v and move forward.

data Zipper t a = ZDone (t a) 
                | Z a (Maybe a -> Zipper t a)

make_zipper :: T.Traversable t => t a -> Zipper t …
Run Code Online (Sandbox Code Playgroud)

monads continuations haskell scala zipper

18
推荐指数
1
解决办法
685
查看次数

为什么Task.WhenAll的continuations是同步执行的?

Task.WhenAll在 .NET Core 3.0 上运行时,我只是对该方法进行了一个奇怪的观察。我将一个简单的Task.Delay任务作为单个参数传递给Task.WhenAll,并且我预计包装的任务将与原始任务的行为相同。但这种情况并非如此。原始任务的延续是异步执行的(这是可取的),多个Task.WhenAll(task)包装器的延续是一个接一个同步执行的(这是不可取的)。

这是此行为的演示。四个工作任务正在等待同一个Task.Delay任务完成,然后继续进行繁重的计算(由 a 模拟Thread.Sleep)。

var task = Task.Delay(500);
var workers = Enumerable.Range(1, 4).Select(async x =>
{
    Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
        $" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} before await");

    await task;
    //await Task.WhenAll(task);

    Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
        $" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} after await");

    Thread.Sleep(1000); // Simulate some heavy CPU-bound computation
}).ToArray();
Task.WaitAll(workers);
Run Code Online (Sandbox Code Playgroud)

这是输出。四个延续在不同的线程(并行)中按预期运行。

var task = Task.Delay(500);
var workers = Enumerable.Range(1, 4).Select(async x =>
{
    Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}" +
        $" [{Thread.CurrentThread.ManagedThreadId}] Worker{x} before await"); …
Run Code Online (Sandbox Code Playgroud)

c# continuations task-parallel-library async-await .net-core

18
推荐指数
1
解决办法
957
查看次数

如何将连续 Monad 分解为左右伴随?

由于状态 monad 可以分解为产品(左 - 函子)和阅读器(右 - 可表示)。

  1. 有没有办法分解 Continuation Monad?下面的代码是我的尝试,它不会类型检查
-- To form a -> (a -> k) -> k
{-# LANGUAGE MultiParamTypeClasses, TypeOperators, InstanceSigs, TypeSynonymInstances #-}
type (<-:) o i = i -> o
-- I Dont think we can have Functor & Representable for this type synonym

class Isomorphism a b where
   from :: a -> b
   to :: b -> a

instance Adjunction ((<-:) e) ((<-:) e) where
   unit :: a -> (a -> e) -> e …
Run Code Online (Sandbox Code Playgroud)

monads continuations haskell category-theory comonad

18
推荐指数
1
解决办法
1326
查看次数