Continuations

Continuations in Atomo are very simple, and probably more powerful than they should be.

But this is not a language for the faint of heart.

Just keep in mind that when you use continuations, you'd better be careful how you use them - and be sure that you're not using a cannon to hammer a nail. With great power comes great responsibility.

NOTE: A few of these examples need to use blocks to get around limitations of Anatomy's interactive examples, so they may be a bit long.

Continuation  Object

A Continuation is a value that represents the context of an expression that was evaluated; what was being done with it, and whatever was going to happen next.

o call/cc  any
  | o responds-to?: @call:

Sends the current Continuation to o.

Example:

> { return | "before" print; return yield: 100; "after" print } call/cc
before
100

When the continuation is yielded to, the appropriate before/after thunks (provided by before:after:, et al.) are unwound.

o call/cc: (... args)  any
  | o responds-to?: @call:

Like call/cc, but sends some additional arguments along to the block.

The continuation is the first argument, followed by args.

c yield: v  any
  | c is-a?: Continuation

Immediately go back to c's context and yield v in its place.

Example:

> { cc | "before" print; cc yield: 1; "after" print } call/cc
before
1
action before: pre-thunk after: post-thunk  any
  | [action, pre-thunk, post-think] all?: @(is-a?: Block)

Calls the three blocks in order, returning action's result.

In addition; post-thunk is always called, even if an exception is raised.

Example:

> { "in" print } before: { "pre" print } after: { "post" print }
pre
in
post
"in"

pre-thunk is called whenever action is re-entered (e.g. by jumping back into it by yielding to a continuation). post-thunk is called whenever control leaves the block (e.g. by yielding to another continuation) or when action completes.

Example:

> yield!: c := when: (c is-a?: Continuation) do: { c yield }
@ok
> x := { "in" print; current-continuation } before: { "pre" print } after: { "post" print }
@ok
> { cont = x; cont print; yield!: cont } call
pre
in
post
<continuation>
pre
post
@ok
@ok
action after: post-thunk  any
  | action is-a?: Block
  | post-thunk is-a?: Block

Shortcut for action before: { @ok } after: post-thunk.

action before: pre-thunk  any
  | action is-a?: Block
  | pre-thunk is-a?: Block

Shortcut for action before: pre-thunk after: { @ok }.

init wrap: cleanup do: action  any
  | init is-a?: Block
  | cleanup responds-to?: @call:
  | action responds-to?: @call:

Calls action with the init's result, ensuring that cleanup is performed when control leaves action and that init is re-calleded and passed to action when control enters action.

See also before:after:.

Example:

> { "init" print } wrap: { c | c uppercase print } do: { c | @(action: c) print }
init
@(action: "init")
INIT
@(action: "init")
current-continuation  Continuation

Returns the current continuation.

Be careful, though: by current, it really means current. You usually want to use call/cc.

For example, if you assign this to a value, the continuation will be that assignment:

> { a = current-continuation; a print; (a yield: 42) when: (a is-a?: Continuation); @done } call
<continuation>
42
@done

We had to check if a was a Continuation there, because after the yield: it becomes 42, and you'd end up with 42 yield: 42. That is, execution continues from just after the assignment, with a as its new value, which it happily attempts to yield: to all over again.

c call: l  any
  | c is-a?: Continuation
  | (l is-a?: List) && (l length == 1)

A helper that just calls c yield: l head.

c yield  any
  | c is-a?: Continuation

c yield: @ok.

c call  any
  | c is-a?: Continuation

c yield: @ok.