WHEN Block Types and Execution

Block Architecture

WHEN organizes code execution into distinct block types, each with specific behavior and use cases. This replaces traditional loop constructs with declarative execution models.

Main Block

The main: block is the program entry point and runs continuously.

main:
    # Executes every frame/iteration
    when some_condition:
        start_task()

    when task_complete:
        print("Task finished!")
        exit()

Main Block Characteristics

  • Always present: Every WHEN program must have exactly one main block
  • Continuous execution: Runs in a loop until program exit
  • Cooperative scheduling: Orchestrates other blocks
  • State management: Controls overall program flow

Forever Blocks (FO)

Forever blocks run continuously until explicitly stopped.

fo background_task():
    perform_background_work()
    sleep(1)  # Prevent tight loops

fo game_loop():
    update_game_state()
    render_frame()
    sleep(0.016)  # ~60 FPS

    when game_over:
        break  # Exit the block

FO Block Features

  • Infinite execution: Runs until break or .stop()
  • Cooperative: Shares execution time with main thread
  • Parallel option: Use parallel fo for separate thread

Declarative Blocks (DE)

Declarative blocks execute a fixed number of iterations.

de process_items(10):
    current_item = items[process_items.current_iteration]
    process(current_item)
    print(f"Processed {process_items.current_iteration + 1}/10")

# Variable iteration count
item_count = len(my_list)
de dynamic_processor(item_count):
    item = my_list[dynamic_processor.current_iteration]
    handle_item(item)

DE Block Features

  • Fixed iterations: Runs exactly N times
  • Automatic termination: Stops when count reached
  • Iteration tracking: Access via .current_iteration
  • Dynamic counts: Use variables for iteration count

DE Block Properties

de worker(5):
    print(f"Iteration: {worker.current_iteration}")
    print(f"Status: {worker.status}")  # RUNNING, STOPPED, COMPLETED
    print(f"Total iterations: {worker.iterations}")

One-Shot Blocks (OS)

One-shot blocks execute immediately when called, useful for recursive operations.

# Recursive list processing
current_index = 0
items = ["a", "b", "c", "d"]

os process_next():
    when current_index < len(items):
        print(f"Processing: {items[current_index]}")
        current_index = current_index + 1
        process_next()  # Recursive call

main:
    process_next()  # Start processing

OS Block Characteristics

  • Immediate execution: Runs once when called
  • Synchronous: Completes before returning
  • Recursive capable: Can call themselves
  • Stack-safe: Interpreter manages recursion depth

Parallel Execution

Add parallel keyword to run blocks in separate threads.

# Parallel forever block
parallel fo cpu_intensive():
    result = complex_calculation()
    print(f"Calculation complete: {result}")
    break

# Parallel declarative block
parallel de batch_processor(100):
    process_batch_item(batch_processor.current_iteration)
    sleep(0.1)

main:
    cpu_intensive.start()
    batch_processor.start()

    # Main thread remains responsive
    handle_user_input()

Parallel Execution Features

  • True parallelism: Runs in separate OS threads
  • Thread safety: Variables are shared safely
  • Independent execution: Doesn't block main thread
  • Automatic cleanup: Threads managed by interpreter

Block Control Operations

Starting and Stopping

# Start blocks
my_block.start()

# Stop blocks
my_block.stop()

# Check if block is running
when my_block.status == "RUNNING":
    print("Block is active")

State Management

de important_work(20):
    progress = progress + 1

    # Save checkpoint at halfway point
    when important_work.current_iteration == 10:
        important_work.save()

    # Handle error condition
    when error_occurred:
        important_work.savestop()  # Save and stop
        fix_error()
        important_work.startsave()  # Resume from save

    # Discard save when no longer needed
    when progress >= 20:
        important_work.discard()

Save/Restore Operations

  • .save(): Save current execution state
  • .savestop(): Save state and stop block
  • .startsave(): Start from saved state
  • .discard(): Remove saved state

Block Communication

Shared Variables

shared_counter = 0
task_complete = False

fo producer():
    shared_counter = shared_counter + 1
    when shared_counter >= 100:
        task_complete = True
        break

fo consumer():
    when shared_counter > 0:
        process_item(shared_counter)
        shared_counter = shared_counter - 1

    when task_complete:
        break

main:
    producer.start()
    consumer.start()

Event-Driven Patterns

events = []

fo event_generator():
    events.append(generate_event())
    sleep(1)

fo event_processor():
    when len(events) > 0:
        event = events.pop(0)
        handle_event(event)

Error Handling in Blocks

de robust_processor(10):
    success = process_item(robust_processor.current_iteration)

    when not success:
        print("Processing failed, retrying...")
        robust_processor.current_iteration = robust_processor.current_iteration - 1

    when success:
        print("Item processed successfully")

fo resilient_service():
    result = attempt_operation()

    when result["error"]:
        print(f"Service error: {result['error']}")
        sleep(5)  # Wait before retry
        # Block continues running

    when result["success"]:
        handle_success(result["data"])

Performance Considerations

Cooperative vs Parallel

  • Cooperative blocks: Share main thread, good for I/O-bound tasks
  • Parallel blocks: Use separate threads, good for CPU-bound tasks

Block Lifecycle

  1. Creation: Block definition parsed
  2. Start: Execution begins
  3. Running: Active execution phase
  4. Completion/Stop: Block terminates
  5. Cleanup: Resources released

Best Practices

  • Use sleep() in forever blocks to prevent tight loops
  • Prefer DE blocks over FO blocks when iteration count is known
  • Use parallel blocks for independent, CPU-intensive work
  • Save state at logical checkpoints in long-running blocks
  • Handle error conditions gracefully within blocks

Block Introspection

# Access block properties
print(f"Current iteration: {my_block.current_iteration}")
print(f"Block status: {my_block.status}")
print(f"Has saved state: {my_block.has_saved_state}")

# Conditional logic based on block state
when my_block.status == "COMPLETED":
    print("Block finished successfully")

when my_block.status == "STOPPED":
    print("Block was stopped manually")