Claude
Skills
Sign in
Back

Lua Coroutines

Included with Lifetime
$97 forever

Use when lua coroutines for cooperative multitasking including coroutine creation, yielding and resuming, passing values, generators, iterators, asynchronous patterns, state machines, and producer-consumer implementations.

General

What this skill does


# Lua Coroutines

## Introduction

Coroutines in Lua provide cooperative multitasking, enabling functions to
suspend and resume execution. Unlike threads, coroutines don't run in parallel
but yield control explicitly, making them simpler to reason about while enabling
powerful asynchronous patterns without callback complexity.

Coroutines are first-class values in Lua, created from functions and managed
through the coroutine library. They maintain their own stack, local variables,
and instruction pointer, allowing suspension at any point and resumption later.
This enables elegant implementations of generators, iterators, and state machines.

This skill covers coroutine basics, yielding and resuming with values, generators
and iterators, producer-consumer patterns, asynchronous I/O simulation, state
machines, error handling, and practical coroutine patterns.

## Coroutine Fundamentals

Coroutines enable functions to pause and resume execution, providing cooperative
multitasking without thread complexity.

```lua
-- Creating a coroutine
local function simple_task()
  print("Task started")
  coroutine.yield()
  print("Task resumed")
  coroutine.yield()
  print("Task finished")
end

local co = coroutine.create(simple_task)

-- Checking coroutine status
print(coroutine.status(co))  -- "suspended"

-- Resuming coroutine
coroutine.resume(co)  -- Prints "Task started"
print(coroutine.status(co))  -- "suspended"

coroutine.resume(co)  -- Prints "Task resumed"
coroutine.resume(co)  -- Prints "Task finished"
print(coroutine.status(co))  -- "dead"

-- Coroutine with parameters
local function greet(name)
  print("Hello, " .. name)
  local response = coroutine.yield("What's your age?")
  print(name .. " is " .. response .. " years old")
end

local co2 = coroutine.create(greet)
local success, question = coroutine.resume(co2, "Alice")
print(question)  -- "What's your age?"

coroutine.resume(co2, 30)  -- "Alice is 30 years old"

-- Coroutine returning values
local function counter()
  for i = 1, 5 do
    coroutine.yield(i)
  end
  return "done"
end

local co3 = coroutine.create(counter)
repeat
  local success, value = coroutine.resume(co3)
  print(value)
until coroutine.status(co3) == "dead"

-- Coroutine wrap (simpler interface)
local function wrapped_task()
  for i = 1, 3 do
    coroutine.yield(i * 10)
  end
end

local f = coroutine.wrap(wrapped_task)
print(f())  -- 10
print(f())  -- 20
print(f())  -- 30

-- Running coroutine
local function self_aware()
  if coroutine.running() then
    print("Running in coroutine")
  else
    print("Running in main")
  end
end

self_aware()  -- "Running in main"
coroutine.resume(coroutine.create(self_aware))  -- "Running in coroutine"

-- Yielding from nested calls
local function inner()
  print("Inner start")
  coroutine.yield("from inner")
  print("Inner end")
end

local function outer()
  print("Outer start")
  inner()
  print("Outer end")
end

local co4 = coroutine.create(outer)
coroutine.resume(co4)  -- Prints "Outer start" and "Inner start"
coroutine.resume(co4)  -- Prints "Inner end" and "Outer end"

-- Bidirectional communication
local function echo()
  while true do
    local value = coroutine.yield()
    if value == nil then break end
    print("Echo: " .. value)
  end
end

local co5 = coroutine.create(echo)
coroutine.resume(co5)
coroutine.resume(co5, "Hello")
coroutine.resume(co5, "World")
coroutine.resume(co5)  -- nil terminates

-- Error handling in coroutines
local function faulty()
  print("Before error")
  error("Something went wrong")
  print("After error")  -- Never executes
end

local co6 = coroutine.create(faulty)
local success, err = coroutine.resume(co6)
if not success then
  print("Error caught: " .. err)
end
```

Coroutines enable cooperative multitasking where functions explicitly yield
control rather than being preempted.

## Generators and Iterators

Coroutines elegantly implement generators and custom iterators for lazy
evaluation and infinite sequences.

```lua
-- Simple generator
local function range(from, to, step)
  step = step or 1
  return coroutine.wrap(function()
    for i = from, to, step do
      coroutine.yield(i)
    end
  end)
end

for n in range(1, 10, 2) do
  print(n)  -- 1, 3, 5, 7, 9
end

-- Infinite generator
local function naturals()
  return coroutine.wrap(function()
    local n = 1
    while true do
      coroutine.yield(n)
      n = n + 1
    end
  end)
end

local gen = naturals()
print(gen())  -- 1
print(gen())  -- 2
print(gen())  -- 3

-- Fibonacci generator
local function fibonacci()
  return coroutine.wrap(function()
    local a, b = 0, 1
    while true do
      coroutine.yield(a)
      a, b = b, a + b
    end
  end)
end

local fib = fibonacci()
for i = 1, 10 do
  print(fib())
end

-- Filter generator
local function filter(gen, predicate)
  return coroutine.wrap(function()
    for value in gen do
      if predicate(value) then
        coroutine.yield(value)
      end
    end
  end)
end

local evens = filter(range(1, 20), function(n) return n % 2 == 0 end)
for n in evens do
  print(n)  -- 2, 4, 6, 8, 10, 12, 14, 16, 18, 20
end

-- Map generator
local function map(gen, transform)
  return coroutine.wrap(function()
    for value in gen do
      coroutine.yield(transform(value))
    end
  end)
end

local squared = map(range(1, 5), function(n) return n * n end)
for n in squared do
  print(n)  -- 1, 4, 9, 16, 25
end

-- Take generator (limit results)
local function take(gen, n)
  return coroutine.wrap(function()
    local count = 0
    for value in gen do
      if count >= n then break end
      coroutine.yield(value)
      count = count + 1
    end
  end)
end

local first5 = take(naturals(), 5)
for n in first5 do
  print(n)  -- 1, 2, 3, 4, 5
end

-- Chain generators
local function chain(...)
  local generators = {...}
  return coroutine.wrap(function()
    for _, gen in ipairs(generators) do
      for value in gen do
        coroutine.yield(value)
      end
    end
  end)
end

local combined = chain(range(1, 3), range(10, 12))
for n in combined do
  print(n)  -- 1, 2, 3, 10, 11, 12
end

-- Zip generators
local function zip(gen1, gen2)
  return coroutine.wrap(function()
    while true do
      local v1 = gen1()
      local v2 = gen2()
      if v1 == nil or v2 == nil then break end
      coroutine.yield(v1, v2)
    end
  end)
end

local letters = coroutine.wrap(function()
  for c in string.gmatch("abc", ".") do
    coroutine.yield(c)
  end
end)

local zipped = zip(range(1, 3), letters)
for num, letter in zipped do
  print(num, letter)  -- 1 a, 2 b, 3 c
end

-- Permutation generator
local function permute(array)
  return coroutine.wrap(function()
    local function perm(arr, n)
      n = n or #arr
      if n == 1 then
        coroutine.yield(arr)
      else
        for i = 1, n do
          arr[n], arr[i] = arr[i], arr[n]
          perm(arr, n - 1)
          arr[n], arr[i] = arr[i], arr[n]
        end
      end
    end

    local copy = {}
    for i, v in ipairs(array) do
      copy[i] = v
    end
    perm(copy)
  end)
end

for perm in permute({1, 2, 3}) do
  print(table.concat(perm, ", "))
end

-- File line iterator
local function lines(filename)
  return coroutine.wrap(function()
    local file = io.open(filename, "r")
    if not file then return end

    for line in file:lines() do
      coroutine.yield(line)
    end

    file:close()
  end)
end
```

Generators enable lazy evaluation and infinite sequences with clean, readable
syntax.

## Producer-Consumer Pattern

Coroutines elegantly implement producer-consumer patterns without explicit
queues or callbacks.

```lua
-- Basic producer-consumer
local function producer()
  return coroutine.create(function()
    for i = 1, 10 do
      print("Producing " .. i)
      coroutine.yield(i)
    end
  end)
end

local function consumer(prod)
  while coroutine.status(prod) ~= "dead" do
    local success, value = coroutine.resume(prod)
    if success and value then
      print("Consuming " .. value)
    end
  end
end

local prod = producer()
c

Related in General