Coroutines#

Coroutines are a programming concept consisting of function suspension (suspend) and resumption, and the spawning of asynchronous processing (coroutine launch).

Launching Coroutines#

You can launch a new coroutine using the LAUNCH function.

LAUNCH: Launch a New Coroutine#

<T> LAUNCH(function: () -> T): PROMISE<T>

Launches function asynchronously as a coroutine.

The launched coroutine is executed when the thread that called LAUNCH next suspends.

This function returns a PROMISE that stores the return value of function or the value thrown within function.

If any value is thrown within function, that value is also output to standard error output.

$ xa '
  promise := LAUNCH ( =>
    "apple"
  )
  promise::await()
'
# apple

function is launched independently of the caller and is executed as soon as the calling thread is suspended.

$ xa '
  result := PROMISE.new()
  LAUNCH ( =>
    result::complete("apple")
  )
  result::await()
'
# apple

If the return value of function is a stream, it automatically iterates only once, holds a copy of the element sequence, and returns a copied stream upon await.

Therefore, the processing at the end of the LAUNCH block is always executed exactly once in any situation.

$ xa '
  promises := [PROMISE.new(), PROMISE.new(), PROMISE.new()]
  LAUNCH ( =>
    0 .. 2 | (
      promises(_)::complete(_ * 10)
    )
  )
  promises(0)::await(), promises(1)::await(), promises(2)::await()
'
# 0
# 10
# 20
$ xa '
  counter := 0
  promise := LAUNCH ( =>
    1 .. 3 | (
      counter = counter + 1
      counter
    )
  )
  [promise::await()], [promise::await()], [promise::await()], counter
'
# [1;2;3]
# [1;2;3]
# [1;2;3]
# 3

Here is a sample using LAUNCH to asynchronously accept commands from standard input.

If you remove the first block part, the program will actually terminate with the user’s stop command.

$ { sleep 0.5; echo stop; } | xa -q '
  stop := PROMISE.new()
  LAUNCH ( =>
    IN | (
      _ == "stop" && (
        OUT << "Stopping..."
        stop::complete()
        break!!
      )
    ) !: break
  )
  LOOP | i, _ => (
    stop::isCompleted() && break!!
    SLEEP << 100
  ) !: break
  OUT << "Stopped!"
'
# Stopping...
# Stopped!

PROMISE: Asynchronous Result Container#

PROMISE is a container whose contents are determined with delay.

new: Generate a New PROMISE#

<T> PROMISE.new(): PROMISE<T>

Generates a new incomplete PROMISE.

complete: Complete a PROMISE#

<T> PROMISE<T>::complete([value: T]): NULL

Completes the PROMISE with the contents of VALUE.

If value is omitted, the PROMISE is completed with NULL as its contents.

fail: Complete a PROMISE as Failed#

<T> PROMISE<T>::fail([error: VALUE]): NULL

Completes the PROMISE as failed with error.

await: Wait for PROMISE Completion and Retrieve Contents#

<T> PROMISE<T>::await(): T

Waits until the contents of the PROMISE are complete and returns the contents.


If the PROMISE is completed as failed, await throws that exception value.

$ xa '
  promise := PROMISE.new()
  promise::fail("ERROR!!")
  promise::await() !? (e => e)
'
# ERROR!!

isCompleted: Check PROMISE Completion Status#

<T> PROMISE<T>::isCompleted(): BOOLEAN

Returns whether the PROMISE is completed or completed as failed.

SLEEP: Suspend Processing for a Specified Time#

SLEEP([milliseconds: NUMBER]): NULL

Suspends processing for milliseconds.

This function does not block the thread but suspends the function.

If milliseconds is 0 or omitted, the function suspends once and returns immediately.


In the following sample code, Hello, World! is output after 1 second from execution.

$ xa '
  SLEEP(1000)
  "Hello, World!"
'
# Hello, World!

GENERATE: Generate Stream from Function#

GENERATE(generator: (yield: (value: VALUE) -> NULL) -> NULL | STREAM): STREAM<VALUE>

Executes the generator function of the first argument and returns the values passed to the yield function within that function as a stream.

If the yield function returns a stream, that stream is iterated only once.

The yield function suspends when called.

$ xa '
  GENERATE ( yield =>
    yield << 1
    yield << 2
    yield << 3
  )
'
# 1
# 2
# 3