Skip to content

Instantly share code, notes, and snippets.

@catern
Created January 28, 2018 22:20
Show Gist options
  • Save catern/2674d8bc26978a3e6ef927cf3f5fa42e to your computer and use it in GitHub Desktop.
Save catern/2674d8bc26978a3e6ef927cf3f5fa42e to your computer and use it in GitHub Desktop.
def start(coroutine):
"""Starts a coroutine running.
If the coroutine makes a blocking call before yielding for the
first time, start() will block. However, if the coroutine is
written to not use blocking operations, or at least doesn't call
them before its first yield, start() will return immediately
(after reaching the first yield).
In either case, this function will return None.
"""
def run(continuation, value):
try:
# Pass the value in to the coroutine, and the coroutine will run until
# either the coroutine yields us a function to call...
func = continuation(value)
except:
# ...or the coroutine throws an exception, bringing its story to an end.
pass
else:
trampoline(func)
def trampoline(func):
# The coroutine has sent us function "func"; we will pass the coroutine's
# continuations to func, so that it may call them and restart the coroutine.
once = CallOnlyOnce("Only one of the continuations produced by callcc can be called, and only once.")
send = once.wrap(lambda val: run(coroutine.send, val))
throw = once.wrap(lambda exn: run(coroutine.throw, exn))
try:
# Now, actually call the function that the coroutine yielded us.
# We pass it wrapped callbacks for coroutine.send and coroutine.throw, so
# the function may send a value or throw an exception into the coroutine
# as it wishes.
func(send, throw)
# func shouldn't block for overly long, so trampoline(func) will return quickly.
except:
raise RuntimeError("Functions called with callcc are not allowed to throw exceptions")
run(coroutine.send, None)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment