[{"data":1,"prerenderedAt":4672},["ShallowReactive",2],{"page-\u002Flua\u002F11-coroutines":3},{"id":4,"title":5,"body":6,"description":4663,"extension":4664,"meta":4665,"navigation":221,"path":4666,"seo":4667,"stem":4670,"__hash__":4671},"content\u002Flua\u002F11-coroutines.md","Lua 11 — Coroutines, Cooperative Multitasking & Generators",{"type":7,"value":8,"toc":4657},"minimark",[9,14,55,60,4252,4264,4268,4274,4375,4381,4426,4438,4448,4452,4653],[10,11,13],"h1",{"id":12},"_11-coroutines-cooperative-multitasking-generators","11 — Coroutines, Cooperative Multitasking & Generators",[15,16,17,18,22,23,27,28,31,32,35,36,35,39,42,43,46,47,50,51,54],"p",{},"Coroutines are Lua's ",[19,20,21],"strong",{},"cooperative multitasking"," primitive. A coroutine is a function with its own stack that can suspend (",[24,25,26],"code",{},"coroutine.yield",") and resume (",[24,29,30],{},"coroutine.resume","). Unlike threads (preemptive), coroutines yield control explicitly. This makes them perfect for ",[19,33,34],{},"generators",", ",[19,37,38],{},"lazy iterators",[19,40,41],{},"producer-consumer pipelines",", and ",[19,44,45],{},"async-style code"," without callbacks. Lua's coroutines are ",[19,48,49],{},"symmetric"," (one resume ↔ one yield) and ",[19,52,53],{},"stackful"," (can yield from nested calls).",[56,57,59],"h2",{"id":58},"coroutine-lifecycle","Coroutine Lifecycle",[61,62,64,422,426],"code-wrapper",{"language":63},"lua",[65,66,70],"pre",{"className":67,"code":68,"language":63,"meta":69,"style":69},"language-lua shiki shiki-themes github-light github-dark","-- coroutine.create(fn): create a coroutine (type \"thread\", NOT OS thread)\n-- Returns a coroutine object, status \"suspended\"\nlocal co = coroutine.create(function()\n  print(\"running\")       -- executes on first resume\n  coroutine.yield(1)     -- suspend, return 1 to resumer\n  print(\"resumed\")       -- executes on second resume\n  coroutine.yield(2)     -- suspend, return 2\n  print(\"done\")          -- executes on third resume\n  return \"final\"         -- return value goes to last resume\nend)\n\n-- coroutine.status(co): \"suspended\" | \"running\" | \"normal\" | \"dead\"\nprint(coroutine.status(co))  -- \"suspended\" (before first resume)\n\n-- coroutine.resume(co, args...): start or continue the coroutine\n-- Returns: true, yield_values... OR false, error_message\nlocal ok1, val1 = coroutine.resume(co)  -- runs to first yield; prints \"running\"; val1 = 1\nprint(ok1, val1)                         -- true  1\nprint(coroutine.status(co))             -- \"suspended\"\n\nlocal ok2, val2 = coroutine.resume(co)  -- runs to second yield; prints \"resumed\"; val2 = 2\nprint(ok2, val2)                         -- true  2\n\nlocal ok3, val3 = coroutine.resume(co)  -- runs to return; prints \"done\"; val3 = \"final\"\nprint(ok3, val3)                        -- true  \"final\"\nprint(coroutine.status(co))            -- \"dead\" (function completed)\n\nlocal ok4, err = coroutine.resume(co)  -- cannot resume a dead coroutine\nprint(ok4, err)                         -- false  \"cannot resume dead coroutine\"\n","",[24,71,72,81,87,114,132,149,164,179,195,207,216,223,229,246,251,257,263,282,293,308,313,330,341,346,363,374,389,394,411],{"__ignoreMap":69},[73,74,77],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"sdCPZ","-- coroutine.create(fn): create a coroutine (type \"thread\", NOT OS thread)\n",[73,82,84],{"class":75,"line":83},2,[73,85,86],{"class":79},"-- Returns a coroutine object, status \"suspended\"\n",[73,88,90,94,98,101,105,108,111],{"class":75,"line":89},3,[73,91,93],{"class":92},"svdQ7","local",[73,95,97],{"class":96},"ssxIu"," co ",[73,99,100],{"class":92},"=",[73,102,104],{"class":103},"snvgF"," coroutine.create",[73,106,107],{"class":96},"(",[73,109,110],{"class":92},"function",[73,112,113],{"class":96},"()\n",[73,115,117,120,122,126,129],{"class":75,"line":116},4,[73,118,119],{"class":103},"  print",[73,121,107],{"class":96},[73,123,125],{"class":124},"sJ6F3","\"running\"",[73,127,128],{"class":96},")       ",[73,130,131],{"class":79},"-- executes on first resume\n",[73,133,135,138,140,143,146],{"class":75,"line":134},5,[73,136,137],{"class":103},"  coroutine.yield",[73,139,107],{"class":96},[73,141,142],{"class":103},"1",[73,144,145],{"class":96},")     ",[73,147,148],{"class":79},"-- suspend, return 1 to resumer\n",[73,150,152,154,156,159,161],{"class":75,"line":151},6,[73,153,119],{"class":103},[73,155,107],{"class":96},[73,157,158],{"class":124},"\"resumed\"",[73,160,128],{"class":96},[73,162,163],{"class":79},"-- executes on second resume\n",[73,165,167,169,171,174,176],{"class":75,"line":166},7,[73,168,137],{"class":103},[73,170,107],{"class":96},[73,172,173],{"class":103},"2",[73,175,145],{"class":96},[73,177,178],{"class":79},"-- suspend, return 2\n",[73,180,182,184,186,189,192],{"class":75,"line":181},8,[73,183,119],{"class":103},[73,185,107],{"class":96},[73,187,188],{"class":124},"\"done\"",[73,190,191],{"class":96},")          ",[73,193,194],{"class":79},"-- executes on third resume\n",[73,196,198,201,204],{"class":75,"line":197},9,[73,199,200],{"class":92},"  return",[73,202,203],{"class":124}," \"final\"         ",[73,205,206],{"class":79},"-- return value goes to last resume\n",[73,208,210,213],{"class":75,"line":209},10,[73,211,212],{"class":92},"end",[73,214,215],{"class":96},")\n",[73,217,219],{"class":75,"line":218},11,[73,220,222],{"emptyLinePlaceholder":221},true,"\n",[73,224,226],{"class":75,"line":225},12,[73,227,228],{"class":79},"-- coroutine.status(co): \"suspended\" | \"running\" | \"normal\" | \"dead\"\n",[73,230,232,235,237,240,243],{"class":75,"line":231},13,[73,233,234],{"class":103},"print",[73,236,107],{"class":96},[73,238,239],{"class":103},"coroutine.status",[73,241,242],{"class":96},"(co))  ",[73,244,245],{"class":79},"-- \"suspended\" (before first resume)\n",[73,247,249],{"class":75,"line":248},14,[73,250,222],{"emptyLinePlaceholder":221},[73,252,254],{"class":75,"line":253},15,[73,255,256],{"class":79},"-- coroutine.resume(co, args...): start or continue the coroutine\n",[73,258,260],{"class":75,"line":259},16,[73,261,262],{"class":79},"-- Returns: true, yield_values... OR false, error_message\n",[73,264,266,268,271,273,276,279],{"class":75,"line":265},17,[73,267,93],{"class":92},[73,269,270],{"class":96}," ok1, val1 ",[73,272,100],{"class":92},[73,274,275],{"class":103}," coroutine.resume",[73,277,278],{"class":96},"(co)  ",[73,280,281],{"class":79},"-- runs to first yield; prints \"running\"; val1 = 1\n",[73,283,285,287,290],{"class":75,"line":284},18,[73,286,234],{"class":103},[73,288,289],{"class":96},"(ok1, val1)                         ",[73,291,292],{"class":79},"-- true  1\n",[73,294,296,298,300,302,305],{"class":75,"line":295},19,[73,297,234],{"class":103},[73,299,107],{"class":96},[73,301,239],{"class":103},[73,303,304],{"class":96},"(co))             ",[73,306,307],{"class":79},"-- \"suspended\"\n",[73,309,311],{"class":75,"line":310},20,[73,312,222],{"emptyLinePlaceholder":221},[73,314,316,318,321,323,325,327],{"class":75,"line":315},21,[73,317,93],{"class":92},[73,319,320],{"class":96}," ok2, val2 ",[73,322,100],{"class":92},[73,324,275],{"class":103},[73,326,278],{"class":96},[73,328,329],{"class":79},"-- runs to second yield; prints \"resumed\"; val2 = 2\n",[73,331,333,335,338],{"class":75,"line":332},22,[73,334,234],{"class":103},[73,336,337],{"class":96},"(ok2, val2)                         ",[73,339,340],{"class":79},"-- true  2\n",[73,342,344],{"class":75,"line":343},23,[73,345,222],{"emptyLinePlaceholder":221},[73,347,349,351,354,356,358,360],{"class":75,"line":348},24,[73,350,93],{"class":92},[73,352,353],{"class":96}," ok3, val3 ",[73,355,100],{"class":92},[73,357,275],{"class":103},[73,359,278],{"class":96},[73,361,362],{"class":79},"-- runs to return; prints \"done\"; val3 = \"final\"\n",[73,364,366,368,371],{"class":75,"line":365},25,[73,367,234],{"class":103},[73,369,370],{"class":96},"(ok3, val3)                        ",[73,372,373],{"class":79},"-- true  \"final\"\n",[73,375,377,379,381,383,386],{"class":75,"line":376},26,[73,378,234],{"class":103},[73,380,107],{"class":96},[73,382,239],{"class":103},[73,384,385],{"class":96},"(co))            ",[73,387,388],{"class":79},"-- \"dead\" (function completed)\n",[73,390,392],{"class":75,"line":391},27,[73,393,222],{"emptyLinePlaceholder":221},[73,395,397,399,402,404,406,408],{"class":75,"line":396},28,[73,398,93],{"class":92},[73,400,401],{"class":96}," ok4, err ",[73,403,100],{"class":92},[73,405,275],{"class":103},[73,407,278],{"class":96},[73,409,410],{"class":79},"-- cannot resume a dead coroutine\n",[73,412,414,416,419],{"class":75,"line":413},29,[73,415,234],{"class":103},[73,417,418],{"class":96},"(ok4, err)                         ",[73,420,421],{"class":79},"-- false  \"cannot resume dead coroutine\"\n",[56,423,425],{"id":424},"the-resume-yield-value-protocol","The Resume-Yield Value Protocol",[61,427,428,648,655],{"language":63},[65,429,431],{"className":67,"code":430,"language":63,"meta":69,"style":69},"-- Values flow BOTH directions through resume\u002Fyield:\n-- resume(args) → yield receives args (first resume starts the function with these args)\n-- yield(values) → resume returns these values\n-- return value → final resume returns this value\n\nlocal co = coroutine.create(function(a, b)  -- a, b from FIRST resume\n  print(\"start:\", a, b)                       -- \"start: 1 2\"\n  local c, d = coroutine.yield(a + b)         -- yields a+b; c,d from SECOND resume\n  print(\"resumed with:\", c, d)               -- \"resumed with: 10 20\"\n  return c * d                               -- final return → third resume\nend)\n\nlocal r1 = coroutine.resume(co, 1, 2)       -- start: a=1, b=2; yields 3\nprint(r1)                                    -- true  3 (a+b)\n\nlocal r2 = coroutine.resume(co, 10, 20)      -- continue: c=10, d=20 (passed to yield)\nprint(r2)                                    -- true  200 (c*d, the return value)\n\n-- Key: the FIRST resume's args go to the function parameters.\n-- Subsequent resume's args go to the yield() expressions.\n",[24,432,433,438,443,448,453,457,477,492,517,532,548,554,558,583,593,597,624,634,638,643],{"__ignoreMap":69},[73,434,435],{"class":75,"line":76},[73,436,437],{"class":79},"-- Values flow BOTH directions through resume\u002Fyield:\n",[73,439,440],{"class":75,"line":83},[73,441,442],{"class":79},"-- resume(args) → yield receives args (first resume starts the function with these args)\n",[73,444,445],{"class":75,"line":89},[73,446,447],{"class":79},"-- yield(values) → resume returns these values\n",[73,449,450],{"class":75,"line":116},[73,451,452],{"class":79},"-- return value → final resume returns this value\n",[73,454,455],{"class":75,"line":134},[73,456,222],{"emptyLinePlaceholder":221},[73,458,459,461,463,465,467,469,471,474],{"class":75,"line":151},[73,460,93],{"class":92},[73,462,97],{"class":96},[73,464,100],{"class":92},[73,466,104],{"class":103},[73,468,107],{"class":96},[73,470,110],{"class":92},[73,472,473],{"class":96},"(a, b)  ",[73,475,476],{"class":79},"-- a, b from FIRST resume\n",[73,478,479,481,483,486,489],{"class":75,"line":166},[73,480,119],{"class":103},[73,482,107],{"class":96},[73,484,485],{"class":124},"\"start:\"",[73,487,488],{"class":96},", a, b)                       ",[73,490,491],{"class":79},"-- \"start: 1 2\"\n",[73,493,494,497,500,502,505,508,511,514],{"class":75,"line":181},[73,495,496],{"class":92},"  local",[73,498,499],{"class":96}," c, d ",[73,501,100],{"class":92},[73,503,504],{"class":103}," coroutine.yield",[73,506,507],{"class":96},"(a ",[73,509,510],{"class":92},"+",[73,512,513],{"class":96}," b)         ",[73,515,516],{"class":79},"-- yields a+b; c,d from SECOND resume\n",[73,518,519,521,523,526,529],{"class":75,"line":197},[73,520,119],{"class":103},[73,522,107],{"class":96},[73,524,525],{"class":124},"\"resumed with:\"",[73,527,528],{"class":96},", c, d)               ",[73,530,531],{"class":79},"-- \"resumed with: 10 20\"\n",[73,533,534,536,539,542,545],{"class":75,"line":209},[73,535,200],{"class":92},[73,537,538],{"class":96}," c ",[73,540,541],{"class":92},"*",[73,543,544],{"class":96}," d                               ",[73,546,547],{"class":79},"-- final return → third resume\n",[73,549,550,552],{"class":75,"line":218},[73,551,212],{"class":92},[73,553,215],{"class":96},[73,555,556],{"class":75,"line":225},[73,557,222],{"emptyLinePlaceholder":221},[73,559,560,562,565,567,569,572,574,576,578,580],{"class":75,"line":231},[73,561,93],{"class":92},[73,563,564],{"class":96}," r1 ",[73,566,100],{"class":92},[73,568,275],{"class":103},[73,570,571],{"class":96},"(co, ",[73,573,142],{"class":103},[73,575,35],{"class":96},[73,577,173],{"class":103},[73,579,128],{"class":96},[73,581,582],{"class":79},"-- start: a=1, b=2; yields 3\n",[73,584,585,587,590],{"class":75,"line":248},[73,586,234],{"class":103},[73,588,589],{"class":96},"(r1)                                    ",[73,591,592],{"class":79},"-- true  3 (a+b)\n",[73,594,595],{"class":75,"line":253},[73,596,222],{"emptyLinePlaceholder":221},[73,598,599,601,604,606,608,610,613,615,618,621],{"class":75,"line":259},[73,600,93],{"class":92},[73,602,603],{"class":96}," r2 ",[73,605,100],{"class":92},[73,607,275],{"class":103},[73,609,571],{"class":96},[73,611,612],{"class":103},"10",[73,614,35],{"class":96},[73,616,617],{"class":103},"20",[73,619,620],{"class":96},")      ",[73,622,623],{"class":79},"-- continue: c=10, d=20 (passed to yield)\n",[73,625,626,628,631],{"class":75,"line":265},[73,627,234],{"class":103},[73,629,630],{"class":96},"(r2)                                    ",[73,632,633],{"class":79},"-- true  200 (c*d, the return value)\n",[73,635,636],{"class":75,"line":284},[73,637,222],{"emptyLinePlaceholder":221},[73,639,640],{"class":75,"line":295},[73,641,642],{"class":79},"-- Key: the FIRST resume's args go to the function parameters.\n",[73,644,645],{"class":75,"line":310},[73,646,647],{"class":79},"-- Subsequent resume's args go to the yield() expressions.\n",[56,649,651,654],{"id":650},"coroutinewrap-generator-function",[24,652,653],{},"coroutine.wrap"," — Generator Function",[61,656,657,998,1005],{"language":63},[65,658,660],{"className":67,"code":659,"language":63,"meta":69,"style":69},"-- coroutine.wrap(fn): like create, but returns a FUNCTION (not a thread)\n-- Calling the function = resume. Returns yield values directly (or errors).\n-- No `true\u002Ffalse` wrapper — cleaner for generator use.\n\nlocal gen = coroutine.wrap(function()\n  for i = 1, 3 do\n    coroutine.yield(i * 10)  -- yield 10, 20, 30\n  end\nend)\n\nprint(gen())  -- 10\nprint(gen())  -- 20\nprint(gen())  -- 30\nprint(gen())  -- (nothing — coroutine dead; wrap returns nothing, or errors)\n\n-- CRITICAL difference from create+resume:\n-- wrap propagates errors directly (no pcall protection)\n-- resume catches errors, returns false + error message\n\n-- Production: generator function with control flow\nlocal function fibonacci()\n  local a, b = 0, 1\n  return coroutine.wrap(function()\n    while true do\n      coroutine.yield(b)    -- infinite sequence\n      a, b = b, a + b       -- advance\n    end\n  end)\nend\n\nlocal fib = fibonacci()\nfor i = 1, 10 do\n  io.write(fib(), \" \")  -- 1 1 2 3 5 8 13 21 34 55\nend\n",[24,661,662,667,672,677,681,699,720,739,744,750,754,769,782,795,808,812,817,822,827,831,836,849,866,878,888,899,917,922,929,934,939,953,971,993],{"__ignoreMap":69},[73,663,664],{"class":75,"line":76},[73,665,666],{"class":79},"-- coroutine.wrap(fn): like create, but returns a FUNCTION (not a thread)\n",[73,668,669],{"class":75,"line":83},[73,670,671],{"class":79},"-- Calling the function = resume. Returns yield values directly (or errors).\n",[73,673,674],{"class":75,"line":89},[73,675,676],{"class":79},"-- No `true\u002Ffalse` wrapper — cleaner for generator use.\n",[73,678,679],{"class":75,"line":116},[73,680,222],{"emptyLinePlaceholder":221},[73,682,683,685,688,690,693,695,697],{"class":75,"line":134},[73,684,93],{"class":92},[73,686,687],{"class":96}," gen ",[73,689,100],{"class":92},[73,691,692],{"class":103}," coroutine.wrap",[73,694,107],{"class":96},[73,696,110],{"class":92},[73,698,113],{"class":96},[73,700,701,704,707,709,712,714,717],{"class":75,"line":151},[73,702,703],{"class":92},"  for",[73,705,706],{"class":96}," i ",[73,708,100],{"class":92},[73,710,711],{"class":103}," 1",[73,713,35],{"class":96},[73,715,716],{"class":103},"3",[73,718,719],{"class":92}," do\n",[73,721,722,725,728,730,733,736],{"class":75,"line":166},[73,723,724],{"class":103},"    coroutine.yield",[73,726,727],{"class":96},"(i ",[73,729,541],{"class":92},[73,731,732],{"class":103}," 10",[73,734,735],{"class":96},")  ",[73,737,738],{"class":79},"-- yield 10, 20, 30\n",[73,740,741],{"class":75,"line":181},[73,742,743],{"class":92},"  end\n",[73,745,746,748],{"class":75,"line":197},[73,747,212],{"class":92},[73,749,215],{"class":96},[73,751,752],{"class":75,"line":209},[73,753,222],{"emptyLinePlaceholder":221},[73,755,756,758,760,763,766],{"class":75,"line":218},[73,757,234],{"class":103},[73,759,107],{"class":96},[73,761,762],{"class":103},"gen",[73,764,765],{"class":96},"())  ",[73,767,768],{"class":79},"-- 10\n",[73,770,771,773,775,777,779],{"class":75,"line":225},[73,772,234],{"class":103},[73,774,107],{"class":96},[73,776,762],{"class":103},[73,778,765],{"class":96},[73,780,781],{"class":79},"-- 20\n",[73,783,784,786,788,790,792],{"class":75,"line":231},[73,785,234],{"class":103},[73,787,107],{"class":96},[73,789,762],{"class":103},[73,791,765],{"class":96},[73,793,794],{"class":79},"-- 30\n",[73,796,797,799,801,803,805],{"class":75,"line":248},[73,798,234],{"class":103},[73,800,107],{"class":96},[73,802,762],{"class":103},[73,804,765],{"class":96},[73,806,807],{"class":79},"-- (nothing — coroutine dead; wrap returns nothing, or errors)\n",[73,809,810],{"class":75,"line":253},[73,811,222],{"emptyLinePlaceholder":221},[73,813,814],{"class":75,"line":259},[73,815,816],{"class":79},"-- CRITICAL difference from create+resume:\n",[73,818,819],{"class":75,"line":265},[73,820,821],{"class":79},"-- wrap propagates errors directly (no pcall protection)\n",[73,823,824],{"class":75,"line":284},[73,825,826],{"class":79},"-- resume catches errors, returns false + error message\n",[73,828,829],{"class":75,"line":295},[73,830,222],{"emptyLinePlaceholder":221},[73,832,833],{"class":75,"line":310},[73,834,835],{"class":79},"-- Production: generator function with control flow\n",[73,837,838,840,843,847],{"class":75,"line":315},[73,839,93],{"class":92},[73,841,842],{"class":92}," function",[73,844,846],{"class":845},"sIsaT"," fibonacci",[73,848,113],{"class":96},[73,850,851,853,856,858,861,863],{"class":75,"line":332},[73,852,496],{"class":92},[73,854,855],{"class":96}," a, b ",[73,857,100],{"class":92},[73,859,860],{"class":103}," 0",[73,862,35],{"class":96},[73,864,865],{"class":103},"1\n",[73,867,868,870,872,874,876],{"class":75,"line":343},[73,869,200],{"class":92},[73,871,692],{"class":103},[73,873,107],{"class":96},[73,875,110],{"class":92},[73,877,113],{"class":96},[73,879,880,883,886],{"class":75,"line":348},[73,881,882],{"class":92},"    while",[73,884,885],{"class":103}," true",[73,887,719],{"class":92},[73,889,890,893,896],{"class":75,"line":365},[73,891,892],{"class":103},"      coroutine.yield",[73,894,895],{"class":96},"(b)    ",[73,897,898],{"class":79},"-- infinite sequence\n",[73,900,901,904,906,909,911,914],{"class":75,"line":376},[73,902,903],{"class":96},"      a, b ",[73,905,100],{"class":92},[73,907,908],{"class":96}," b, a ",[73,910,510],{"class":92},[73,912,913],{"class":96}," b       ",[73,915,916],{"class":79},"-- advance\n",[73,918,919],{"class":75,"line":391},[73,920,921],{"class":92},"    end\n",[73,923,924,927],{"class":75,"line":396},[73,925,926],{"class":92},"  end",[73,928,215],{"class":96},[73,930,931],{"class":75,"line":413},[73,932,933],{"class":92},"end\n",[73,935,937],{"class":75,"line":936},30,[73,938,222],{"emptyLinePlaceholder":221},[73,940,942,944,947,949,951],{"class":75,"line":941},31,[73,943,93],{"class":92},[73,945,946],{"class":96}," fib ",[73,948,100],{"class":92},[73,950,846],{"class":103},[73,952,113],{"class":96},[73,954,956,959,961,963,965,967,969],{"class":75,"line":955},32,[73,957,958],{"class":92},"for",[73,960,706],{"class":96},[73,962,100],{"class":92},[73,964,711],{"class":103},[73,966,35],{"class":96},[73,968,612],{"class":103},[73,970,719],{"class":92},[73,972,974,977,979,982,985,988,990],{"class":75,"line":973},33,[73,975,976],{"class":103},"  io.write",[73,978,107],{"class":96},[73,980,981],{"class":103},"fib",[73,983,984],{"class":96},"(), ",[73,986,987],{"class":124},"\" \"",[73,989,735],{"class":96},[73,991,992],{"class":79},"-- 1 1 2 3 5 8 13 21 34 55\n",[73,994,996],{"class":75,"line":995},34,[73,997,933],{"class":92},[56,999,1001,1002,1004],{"id":1000},"generators-as-for-loop-iterators","Generators as ",[24,1003,958],{}," Loop Iterators",[61,1006,1007,1314,1318],{"language":63},[65,1008,1010],{"className":67,"code":1009,"language":63,"meta":69,"style":69},"-- A generator function (from wrap) IS an iterator — use directly in generic for\nlocal function range(start, stop, step)\n  step = step or 1\n  return coroutine.wrap(function()\n    for i = start, stop, step do\n      coroutine.yield(i)\n    end\n  end)\nend\n\nfor x in range(1, 5) do print(x) end       -- 1 2 3 4 5\nfor x in range(10, 2, -2) do print(x) end  -- 10 8 6 4 2\n\n-- Generator that produces values lazily (no pre-allocation):\nlocal function squares()\n  local i = 0\n  return coroutine.wrap(function()\n    while true do\n      i = i + 1\n      coroutine.yield(i * i)  -- 1, 4, 9, 16, ... (infinite, lazy)\n    end\n  end)\nend\n\nlocal sq = squares()\nfor i = 1, 5 do print(sq()) end  -- 1 4 9 16 25 (only 5 computed, not infinite)\n",[24,1011,1012,1017,1029,1045,1057,1072,1079,1083,1089,1093,1097,1135,1173,1177,1182,1193,1204,1216,1224,1237,1251,1255,1261,1265,1269,1282],{"__ignoreMap":69},[73,1013,1014],{"class":75,"line":76},[73,1015,1016],{"class":79},"-- A generator function (from wrap) IS an iterator — use directly in generic for\n",[73,1018,1019,1021,1023,1026],{"class":75,"line":83},[73,1020,93],{"class":92},[73,1022,842],{"class":92},[73,1024,1025],{"class":845}," range",[73,1027,1028],{"class":96},"(start, stop, step)\n",[73,1030,1031,1034,1036,1039,1042],{"class":75,"line":89},[73,1032,1033],{"class":96},"  step ",[73,1035,100],{"class":92},[73,1037,1038],{"class":96}," step ",[73,1040,1041],{"class":92},"or",[73,1043,1044],{"class":103}," 1\n",[73,1046,1047,1049,1051,1053,1055],{"class":75,"line":116},[73,1048,200],{"class":92},[73,1050,692],{"class":103},[73,1052,107],{"class":96},[73,1054,110],{"class":92},[73,1056,113],{"class":96},[73,1058,1059,1062,1064,1066,1069],{"class":75,"line":134},[73,1060,1061],{"class":92},"    for",[73,1063,706],{"class":96},[73,1065,100],{"class":92},[73,1067,1068],{"class":96}," start, stop, step ",[73,1070,1071],{"class":92},"do\n",[73,1073,1074,1076],{"class":75,"line":151},[73,1075,892],{"class":103},[73,1077,1078],{"class":96},"(i)\n",[73,1080,1081],{"class":75,"line":166},[73,1082,921],{"class":92},[73,1084,1085,1087],{"class":75,"line":181},[73,1086,926],{"class":92},[73,1088,215],{"class":96},[73,1090,1091],{"class":75,"line":197},[73,1092,933],{"class":92},[73,1094,1095],{"class":75,"line":209},[73,1096,222],{"emptyLinePlaceholder":221},[73,1098,1099,1101,1104,1107,1109,1111,1113,1115,1118,1121,1124,1127,1130,1132],{"class":75,"line":218},[73,1100,958],{"class":92},[73,1102,1103],{"class":96}," x ",[73,1105,1106],{"class":92},"in",[73,1108,1025],{"class":103},[73,1110,107],{"class":96},[73,1112,142],{"class":103},[73,1114,35],{"class":96},[73,1116,1117],{"class":103},"5",[73,1119,1120],{"class":96},") ",[73,1122,1123],{"class":92},"do",[73,1125,1126],{"class":103}," print",[73,1128,1129],{"class":96},"(x) ",[73,1131,212],{"class":92},[73,1133,1134],{"class":79},"       -- 1 2 3 4 5\n",[73,1136,1137,1139,1141,1143,1145,1147,1149,1151,1153,1155,1158,1160,1162,1164,1166,1168,1170],{"class":75,"line":225},[73,1138,958],{"class":92},[73,1140,1103],{"class":96},[73,1142,1106],{"class":92},[73,1144,1025],{"class":103},[73,1146,107],{"class":96},[73,1148,612],{"class":103},[73,1150,35],{"class":96},[73,1152,173],{"class":103},[73,1154,35],{"class":96},[73,1156,1157],{"class":92},"-",[73,1159,173],{"class":103},[73,1161,1120],{"class":96},[73,1163,1123],{"class":92},[73,1165,1126],{"class":103},[73,1167,1129],{"class":96},[73,1169,212],{"class":92},[73,1171,1172],{"class":79},"  -- 10 8 6 4 2\n",[73,1174,1175],{"class":75,"line":231},[73,1176,222],{"emptyLinePlaceholder":221},[73,1178,1179],{"class":75,"line":248},[73,1180,1181],{"class":79},"-- Generator that produces values lazily (no pre-allocation):\n",[73,1183,1184,1186,1188,1191],{"class":75,"line":253},[73,1185,93],{"class":92},[73,1187,842],{"class":92},[73,1189,1190],{"class":845}," squares",[73,1192,113],{"class":96},[73,1194,1195,1197,1199,1201],{"class":75,"line":259},[73,1196,496],{"class":92},[73,1198,706],{"class":96},[73,1200,100],{"class":92},[73,1202,1203],{"class":103}," 0\n",[73,1205,1206,1208,1210,1212,1214],{"class":75,"line":265},[73,1207,200],{"class":92},[73,1209,692],{"class":103},[73,1211,107],{"class":96},[73,1213,110],{"class":92},[73,1215,113],{"class":96},[73,1217,1218,1220,1222],{"class":75,"line":284},[73,1219,882],{"class":92},[73,1221,885],{"class":103},[73,1223,719],{"class":92},[73,1225,1226,1229,1231,1233,1235],{"class":75,"line":295},[73,1227,1228],{"class":96},"      i ",[73,1230,100],{"class":92},[73,1232,706],{"class":96},[73,1234,510],{"class":92},[73,1236,1044],{"class":103},[73,1238,1239,1241,1243,1245,1248],{"class":75,"line":310},[73,1240,892],{"class":103},[73,1242,727],{"class":96},[73,1244,541],{"class":92},[73,1246,1247],{"class":96}," i)  ",[73,1249,1250],{"class":79},"-- 1, 4, 9, 16, ... (infinite, lazy)\n",[73,1252,1253],{"class":75,"line":315},[73,1254,921],{"class":92},[73,1256,1257,1259],{"class":75,"line":332},[73,1258,926],{"class":92},[73,1260,215],{"class":96},[73,1262,1263],{"class":75,"line":343},[73,1264,933],{"class":92},[73,1266,1267],{"class":75,"line":348},[73,1268,222],{"emptyLinePlaceholder":221},[73,1270,1271,1273,1276,1278,1280],{"class":75,"line":365},[73,1272,93],{"class":92},[73,1274,1275],{"class":96}," sq ",[73,1277,100],{"class":92},[73,1279,1190],{"class":103},[73,1281,113],{"class":96},[73,1283,1284,1286,1288,1290,1292,1294,1296,1299,1301,1303,1306,1309,1311],{"class":75,"line":376},[73,1285,958],{"class":92},[73,1287,706],{"class":96},[73,1289,100],{"class":92},[73,1291,711],{"class":103},[73,1293,35],{"class":96},[73,1295,1117],{"class":103},[73,1297,1298],{"class":92}," do",[73,1300,1126],{"class":103},[73,1302,107],{"class":96},[73,1304,1305],{"class":103},"sq",[73,1307,1308],{"class":96},"()) ",[73,1310,212],{"class":92},[73,1312,1313],{"class":79},"  -- 1 4 9 16 25 (only 5 computed, not infinite)\n",[56,1315,1317],{"id":1316},"tree-traversal-with-coroutines","Tree Traversal with Coroutines",[61,1319,1320,1808,1812],{"language":63},[65,1321,1323],{"className":67,"code":1322,"language":63,"meta":69,"style":69},"-- Deep tree traversal: coroutines eliminate explicit stack management\n-- The yield propagates through nested calls (stackful coroutines!)\n\nlocal function tree_node(value, children)\n  return {value = value, children = children or {}}\nend\n\nlocal function preorder(node)\n  return coroutine.wrap(function()\n    local function visit(n)\n      coroutine.yield(n.value)          -- yield this node's value\n      for _, child in ipairs(n.children) do\n        visit(child)                    -- recurse — yield propagates up through coroutine\n      end\n    end\n    visit(node)\n  end)\nend\n\nlocal function postorder(node)\n  return coroutine.wrap(function()\n    local function visit(n)\n      for _, child in ipairs(n.children) do\n        visit(child)\n      end\n      coroutine.yield(n.value)  -- yield AFTER visiting children\n    end\n    visit(node)\n  end)\nend\n\n-- Build a tree:\n--       1\n--      \u002F \\\n--     2   3\n--    \u002F \\   \\\n--   4   5   6\nlocal root = tree_node(1, {\n  tree_node(2, {tree_node(4), tree_node(5)}),\n  tree_node(3, {tree_node(6)}),\n})\n\nfor v in preorder(root) do io.write(v, \" \") end  -- 1 2 4 5 3 6\nprint()\nfor v in postorder(root) do io.write(v, \" \") end -- 4 5 2 6 3 1\nprint()\n\n-- CRITICAL: this is impossible without stackful coroutines.\n-- Python generators can't yield from nested calls without \"yield from\".\n-- Lua coroutines yield transparently through any call depth.\n",[24,1324,1325,1330,1335,1339,1351,1373,1377,1381,1393,1405,1418,1433,1455,1466,1471,1475,1482,1488,1492,1496,1507,1519,1529,1547,1554,1558,1571,1575,1581,1587,1591,1595,1600,1605,1610,1616,1622,1628,1647,1680,1700,1706,1711,1743,1750,1778,1785,1790,1796,1802],{"__ignoreMap":69},[73,1326,1327],{"class":75,"line":76},[73,1328,1329],{"class":79},"-- Deep tree traversal: coroutines eliminate explicit stack management\n",[73,1331,1332],{"class":75,"line":83},[73,1333,1334],{"class":79},"-- The yield propagates through nested calls (stackful coroutines!)\n",[73,1336,1337],{"class":75,"line":89},[73,1338,222],{"emptyLinePlaceholder":221},[73,1340,1341,1343,1345,1348],{"class":75,"line":116},[73,1342,93],{"class":92},[73,1344,842],{"class":92},[73,1346,1347],{"class":845}," tree_node",[73,1349,1350],{"class":96},"(value, children)\n",[73,1352,1353,1355,1358,1360,1363,1365,1368,1370],{"class":75,"line":134},[73,1354,200],{"class":92},[73,1356,1357],{"class":96}," {value ",[73,1359,100],{"class":92},[73,1361,1362],{"class":96}," value, children ",[73,1364,100],{"class":92},[73,1366,1367],{"class":96}," children ",[73,1369,1041],{"class":92},[73,1371,1372],{"class":96}," {}}\n",[73,1374,1375],{"class":75,"line":151},[73,1376,933],{"class":92},[73,1378,1379],{"class":75,"line":166},[73,1380,222],{"emptyLinePlaceholder":221},[73,1382,1383,1385,1387,1390],{"class":75,"line":181},[73,1384,93],{"class":92},[73,1386,842],{"class":92},[73,1388,1389],{"class":845}," preorder",[73,1391,1392],{"class":96},"(node)\n",[73,1394,1395,1397,1399,1401,1403],{"class":75,"line":197},[73,1396,200],{"class":92},[73,1398,692],{"class":103},[73,1400,107],{"class":96},[73,1402,110],{"class":92},[73,1404,113],{"class":96},[73,1406,1407,1410,1412,1415],{"class":75,"line":209},[73,1408,1409],{"class":92},"    local",[73,1411,842],{"class":92},[73,1413,1414],{"class":845}," visit",[73,1416,1417],{"class":96},"(n)\n",[73,1419,1420,1422,1425,1428,1430],{"class":75,"line":218},[73,1421,892],{"class":103},[73,1423,1424],{"class":96},"(n.",[73,1426,1427],{"class":845},"value",[73,1429,191],{"class":96},[73,1431,1432],{"class":79},"-- yield this node's value\n",[73,1434,1435,1438,1441,1443,1446,1448,1451,1453],{"class":75,"line":225},[73,1436,1437],{"class":92},"      for",[73,1439,1440],{"class":96}," _, child ",[73,1442,1106],{"class":92},[73,1444,1445],{"class":103}," ipairs",[73,1447,1424],{"class":96},[73,1449,1450],{"class":845},"children",[73,1452,1120],{"class":96},[73,1454,1071],{"class":92},[73,1456,1457,1460,1463],{"class":75,"line":231},[73,1458,1459],{"class":103},"        visit",[73,1461,1462],{"class":96},"(child)                    ",[73,1464,1465],{"class":79},"-- recurse — yield propagates up through coroutine\n",[73,1467,1468],{"class":75,"line":248},[73,1469,1470],{"class":92},"      end\n",[73,1472,1473],{"class":75,"line":253},[73,1474,921],{"class":92},[73,1476,1477,1480],{"class":75,"line":259},[73,1478,1479],{"class":103},"    visit",[73,1481,1392],{"class":96},[73,1483,1484,1486],{"class":75,"line":265},[73,1485,926],{"class":92},[73,1487,215],{"class":96},[73,1489,1490],{"class":75,"line":284},[73,1491,933],{"class":92},[73,1493,1494],{"class":75,"line":295},[73,1495,222],{"emptyLinePlaceholder":221},[73,1497,1498,1500,1502,1505],{"class":75,"line":310},[73,1499,93],{"class":92},[73,1501,842],{"class":92},[73,1503,1504],{"class":845}," postorder",[73,1506,1392],{"class":96},[73,1508,1509,1511,1513,1515,1517],{"class":75,"line":315},[73,1510,200],{"class":92},[73,1512,692],{"class":103},[73,1514,107],{"class":96},[73,1516,110],{"class":92},[73,1518,113],{"class":96},[73,1520,1521,1523,1525,1527],{"class":75,"line":332},[73,1522,1409],{"class":92},[73,1524,842],{"class":92},[73,1526,1414],{"class":845},[73,1528,1417],{"class":96},[73,1530,1531,1533,1535,1537,1539,1541,1543,1545],{"class":75,"line":343},[73,1532,1437],{"class":92},[73,1534,1440],{"class":96},[73,1536,1106],{"class":92},[73,1538,1445],{"class":103},[73,1540,1424],{"class":96},[73,1542,1450],{"class":845},[73,1544,1120],{"class":96},[73,1546,1071],{"class":92},[73,1548,1549,1551],{"class":75,"line":348},[73,1550,1459],{"class":103},[73,1552,1553],{"class":96},"(child)\n",[73,1555,1556],{"class":75,"line":365},[73,1557,1470],{"class":92},[73,1559,1560,1562,1564,1566,1568],{"class":75,"line":376},[73,1561,892],{"class":103},[73,1563,1424],{"class":96},[73,1565,1427],{"class":845},[73,1567,735],{"class":96},[73,1569,1570],{"class":79},"-- yield AFTER visiting children\n",[73,1572,1573],{"class":75,"line":391},[73,1574,921],{"class":92},[73,1576,1577,1579],{"class":75,"line":396},[73,1578,1479],{"class":103},[73,1580,1392],{"class":96},[73,1582,1583,1585],{"class":75,"line":413},[73,1584,926],{"class":92},[73,1586,215],{"class":96},[73,1588,1589],{"class":75,"line":936},[73,1590,933],{"class":92},[73,1592,1593],{"class":75,"line":941},[73,1594,222],{"emptyLinePlaceholder":221},[73,1596,1597],{"class":75,"line":955},[73,1598,1599],{"class":79},"-- Build a tree:\n",[73,1601,1602],{"class":75,"line":973},[73,1603,1604],{"class":79},"--       1\n",[73,1606,1607],{"class":75,"line":995},[73,1608,1609],{"class":79},"--      \u002F \\\n",[73,1611,1613],{"class":75,"line":1612},35,[73,1614,1615],{"class":79},"--     2   3\n",[73,1617,1619],{"class":75,"line":1618},36,[73,1620,1621],{"class":79},"--    \u002F \\   \\\n",[73,1623,1625],{"class":75,"line":1624},37,[73,1626,1627],{"class":79},"--   4   5   6\n",[73,1629,1631,1633,1636,1638,1640,1642,1644],{"class":75,"line":1630},38,[73,1632,93],{"class":92},[73,1634,1635],{"class":96}," root ",[73,1637,100],{"class":92},[73,1639,1347],{"class":103},[73,1641,107],{"class":96},[73,1643,142],{"class":103},[73,1645,1646],{"class":96},", {\n",[73,1648,1650,1653,1655,1657,1660,1663,1665,1668,1671,1673,1675,1677],{"class":75,"line":1649},39,[73,1651,1652],{"class":103},"  tree_node",[73,1654,107],{"class":96},[73,1656,173],{"class":103},[73,1658,1659],{"class":96},", {",[73,1661,1662],{"class":103},"tree_node",[73,1664,107],{"class":96},[73,1666,1667],{"class":103},"4",[73,1669,1670],{"class":96},"), ",[73,1672,1662],{"class":103},[73,1674,107],{"class":96},[73,1676,1117],{"class":103},[73,1678,1679],{"class":96},")}),\n",[73,1681,1683,1685,1687,1689,1691,1693,1695,1698],{"class":75,"line":1682},40,[73,1684,1652],{"class":103},[73,1686,107],{"class":96},[73,1688,716],{"class":103},[73,1690,1659],{"class":96},[73,1692,1662],{"class":103},[73,1694,107],{"class":96},[73,1696,1697],{"class":103},"6",[73,1699,1679],{"class":96},[73,1701,1703],{"class":75,"line":1702},41,[73,1704,1705],{"class":96},"})\n",[73,1707,1709],{"class":75,"line":1708},42,[73,1710,222],{"emptyLinePlaceholder":221},[73,1712,1714,1716,1719,1721,1723,1726,1728,1731,1734,1736,1738,1740],{"class":75,"line":1713},43,[73,1715,958],{"class":92},[73,1717,1718],{"class":96}," v ",[73,1720,1106],{"class":92},[73,1722,1389],{"class":103},[73,1724,1725],{"class":96},"(root) ",[73,1727,1123],{"class":92},[73,1729,1730],{"class":103}," io.write",[73,1732,1733],{"class":96},"(v, ",[73,1735,987],{"class":124},[73,1737,1120],{"class":96},[73,1739,212],{"class":92},[73,1741,1742],{"class":79},"  -- 1 2 4 5 3 6\n",[73,1744,1746,1748],{"class":75,"line":1745},44,[73,1747,234],{"class":103},[73,1749,113],{"class":96},[73,1751,1753,1755,1757,1759,1761,1763,1765,1767,1769,1771,1773,1775],{"class":75,"line":1752},45,[73,1754,958],{"class":92},[73,1756,1718],{"class":96},[73,1758,1106],{"class":92},[73,1760,1504],{"class":103},[73,1762,1725],{"class":96},[73,1764,1123],{"class":92},[73,1766,1730],{"class":103},[73,1768,1733],{"class":96},[73,1770,987],{"class":124},[73,1772,1120],{"class":96},[73,1774,212],{"class":92},[73,1776,1777],{"class":79}," -- 4 5 2 6 3 1\n",[73,1779,1781,1783],{"class":75,"line":1780},46,[73,1782,234],{"class":103},[73,1784,113],{"class":96},[73,1786,1788],{"class":75,"line":1787},47,[73,1789,222],{"emptyLinePlaceholder":221},[73,1791,1793],{"class":75,"line":1792},48,[73,1794,1795],{"class":79},"-- CRITICAL: this is impossible without stackful coroutines.\n",[73,1797,1799],{"class":75,"line":1798},49,[73,1800,1801],{"class":79},"-- Python generators can't yield from nested calls without \"yield from\".\n",[73,1803,1805],{"class":75,"line":1804},50,[73,1806,1807],{"class":79},"-- Lua coroutines yield transparently through any call depth.\n",[56,1809,1811],{"id":1810},"producer-consumer-pipeline","Producer-Consumer Pipeline",[61,1813,1814,2343,2347],{"language":63},[65,1815,1817],{"className":67,"code":1816,"language":63,"meta":69,"style":69},"-- Pipeline: each stage is a coroutine that receives from the previous and yields to the next\n-- Backpressure: consumer pulls only when ready (cooperative, not push-based)\n\n-- Stage 1: read lines (producer)\nlocal function read_lines(lines)\n  local i = 0\n  return coroutine.wrap(function()\n    while i \u003C #lines do\n      i = i + 1\n      coroutine.yield(lines[i])  -- produce one line\n    end\n  end)\nend\n\n-- Stage 2: filter (transformer)\nlocal function filter(input, predicate)\n  return coroutine.wrap(function()\n    for value in input do  -- pull from upstream\n      if predicate(value) then\n        coroutine.yield(value)  -- re-yield if passes filter\n      end\n    end\n  end)\nend\n\n-- Stage 3: map (transformer)\nlocal function map(input, transform)\n  return coroutine.wrap(function()\n    for value in input do\n      coroutine.yield(transform(value))  -- transform and re-yield\n    end\n  end)\nend\n\n-- Stage 4: sink (consumer)\nlocal function drain(input)\n  local results = {}\n  for value in input do\n    results[#results + 1] = value\n  end\n  return results\nend\n\n-- Compose the pipeline:\nlocal lines = {\"10\", \"hello\", \"20\", \"world\", \"30\", \"40\"}\nlocal numbers = filter(read_lines(lines), function(s) return tonumber(s) ~= nil end)\nlocal doubled = map(numbers, function(s) return tonumber(s) * 2 end)\nlocal result = drain(doubled)\nprint(table.concat(result, \", \"))  -- 20, 40, 60, 80\n-- Lazy: each value flows through the pipeline only when drain pulls it\n",[24,1818,1819,1824,1829,1833,1838,1850,1860,1872,1889,1901,1911,1915,1921,1925,1929,1934,1946,1958,1975,1989,2000,2004,2008,2014,2018,2022,2027,2039,2051,2063,2078,2082,2088,2092,2096,2101,2113,2125,2137,2160,2164,2171,2175,2179,2184,2227,2270,2303,2317,2338],{"__ignoreMap":69},[73,1820,1821],{"class":75,"line":76},[73,1822,1823],{"class":79},"-- Pipeline: each stage is a coroutine that receives from the previous and yields to the next\n",[73,1825,1826],{"class":75,"line":83},[73,1827,1828],{"class":79},"-- Backpressure: consumer pulls only when ready (cooperative, not push-based)\n",[73,1830,1831],{"class":75,"line":89},[73,1832,222],{"emptyLinePlaceholder":221},[73,1834,1835],{"class":75,"line":116},[73,1836,1837],{"class":79},"-- Stage 1: read lines (producer)\n",[73,1839,1840,1842,1844,1847],{"class":75,"line":134},[73,1841,93],{"class":92},[73,1843,842],{"class":92},[73,1845,1846],{"class":845}," read_lines",[73,1848,1849],{"class":96},"(lines)\n",[73,1851,1852,1854,1856,1858],{"class":75,"line":151},[73,1853,496],{"class":92},[73,1855,706],{"class":96},[73,1857,100],{"class":92},[73,1859,1203],{"class":103},[73,1861,1862,1864,1866,1868,1870],{"class":75,"line":166},[73,1863,200],{"class":92},[73,1865,692],{"class":103},[73,1867,107],{"class":96},[73,1869,110],{"class":92},[73,1871,113],{"class":96},[73,1873,1874,1876,1878,1881,1884,1887],{"class":75,"line":181},[73,1875,882],{"class":92},[73,1877,706],{"class":96},[73,1879,1880],{"class":92},"\u003C",[73,1882,1883],{"class":92}," #",[73,1885,1886],{"class":96},"lines ",[73,1888,1071],{"class":92},[73,1890,1891,1893,1895,1897,1899],{"class":75,"line":197},[73,1892,1228],{"class":96},[73,1894,100],{"class":92},[73,1896,706],{"class":96},[73,1898,510],{"class":92},[73,1900,1044],{"class":103},[73,1902,1903,1905,1908],{"class":75,"line":209},[73,1904,892],{"class":103},[73,1906,1907],{"class":96},"(lines[i])  ",[73,1909,1910],{"class":79},"-- produce one line\n",[73,1912,1913],{"class":75,"line":218},[73,1914,921],{"class":92},[73,1916,1917,1919],{"class":75,"line":225},[73,1918,926],{"class":92},[73,1920,215],{"class":96},[73,1922,1923],{"class":75,"line":231},[73,1924,933],{"class":92},[73,1926,1927],{"class":75,"line":248},[73,1928,222],{"emptyLinePlaceholder":221},[73,1930,1931],{"class":75,"line":253},[73,1932,1933],{"class":79},"-- Stage 2: filter (transformer)\n",[73,1935,1936,1938,1940,1943],{"class":75,"line":259},[73,1937,93],{"class":92},[73,1939,842],{"class":92},[73,1941,1942],{"class":845}," filter",[73,1944,1945],{"class":96},"(input, predicate)\n",[73,1947,1948,1950,1952,1954,1956],{"class":75,"line":265},[73,1949,200],{"class":92},[73,1951,692],{"class":103},[73,1953,107],{"class":96},[73,1955,110],{"class":92},[73,1957,113],{"class":96},[73,1959,1960,1962,1965,1967,1970,1972],{"class":75,"line":284},[73,1961,1061],{"class":92},[73,1963,1964],{"class":96}," value ",[73,1966,1106],{"class":92},[73,1968,1969],{"class":96}," input ",[73,1971,1123],{"class":92},[73,1973,1974],{"class":79},"  -- pull from upstream\n",[73,1976,1977,1980,1983,1986],{"class":75,"line":295},[73,1978,1979],{"class":92},"      if",[73,1981,1982],{"class":103}," predicate",[73,1984,1985],{"class":96},"(value) ",[73,1987,1988],{"class":92},"then\n",[73,1990,1991,1994,1997],{"class":75,"line":310},[73,1992,1993],{"class":103},"        coroutine.yield",[73,1995,1996],{"class":96},"(value)  ",[73,1998,1999],{"class":79},"-- re-yield if passes filter\n",[73,2001,2002],{"class":75,"line":315},[73,2003,1470],{"class":92},[73,2005,2006],{"class":75,"line":332},[73,2007,921],{"class":92},[73,2009,2010,2012],{"class":75,"line":343},[73,2011,926],{"class":92},[73,2013,215],{"class":96},[73,2015,2016],{"class":75,"line":348},[73,2017,933],{"class":92},[73,2019,2020],{"class":75,"line":365},[73,2021,222],{"emptyLinePlaceholder":221},[73,2023,2024],{"class":75,"line":376},[73,2025,2026],{"class":79},"-- Stage 3: map (transformer)\n",[73,2028,2029,2031,2033,2036],{"class":75,"line":391},[73,2030,93],{"class":92},[73,2032,842],{"class":92},[73,2034,2035],{"class":845}," map",[73,2037,2038],{"class":96},"(input, transform)\n",[73,2040,2041,2043,2045,2047,2049],{"class":75,"line":396},[73,2042,200],{"class":92},[73,2044,692],{"class":103},[73,2046,107],{"class":96},[73,2048,110],{"class":92},[73,2050,113],{"class":96},[73,2052,2053,2055,2057,2059,2061],{"class":75,"line":413},[73,2054,1061],{"class":92},[73,2056,1964],{"class":96},[73,2058,1106],{"class":92},[73,2060,1969],{"class":96},[73,2062,1071],{"class":92},[73,2064,2065,2067,2069,2072,2075],{"class":75,"line":936},[73,2066,892],{"class":103},[73,2068,107],{"class":96},[73,2070,2071],{"class":103},"transform",[73,2073,2074],{"class":96},"(value))  ",[73,2076,2077],{"class":79},"-- transform and re-yield\n",[73,2079,2080],{"class":75,"line":941},[73,2081,921],{"class":92},[73,2083,2084,2086],{"class":75,"line":955},[73,2085,926],{"class":92},[73,2087,215],{"class":96},[73,2089,2090],{"class":75,"line":973},[73,2091,933],{"class":92},[73,2093,2094],{"class":75,"line":995},[73,2095,222],{"emptyLinePlaceholder":221},[73,2097,2098],{"class":75,"line":1612},[73,2099,2100],{"class":79},"-- Stage 4: sink (consumer)\n",[73,2102,2103,2105,2107,2110],{"class":75,"line":1618},[73,2104,93],{"class":92},[73,2106,842],{"class":92},[73,2108,2109],{"class":845}," drain",[73,2111,2112],{"class":96},"(input)\n",[73,2114,2115,2117,2120,2122],{"class":75,"line":1624},[73,2116,496],{"class":92},[73,2118,2119],{"class":96}," results ",[73,2121,100],{"class":92},[73,2123,2124],{"class":96}," {}\n",[73,2126,2127,2129,2131,2133,2135],{"class":75,"line":1630},[73,2128,703],{"class":92},[73,2130,1964],{"class":96},[73,2132,1106],{"class":92},[73,2134,1969],{"class":96},[73,2136,1071],{"class":92},[73,2138,2139,2142,2145,2148,2150,2152,2155,2157],{"class":75,"line":1649},[73,2140,2141],{"class":96},"    results[",[73,2143,2144],{"class":92},"#",[73,2146,2147],{"class":96},"results ",[73,2149,510],{"class":92},[73,2151,711],{"class":103},[73,2153,2154],{"class":96},"] ",[73,2156,100],{"class":92},[73,2158,2159],{"class":96}," value\n",[73,2161,2162],{"class":75,"line":1682},[73,2163,743],{"class":92},[73,2165,2166,2168],{"class":75,"line":1702},[73,2167,200],{"class":92},[73,2169,2170],{"class":96}," results\n",[73,2172,2173],{"class":75,"line":1708},[73,2174,933],{"class":92},[73,2176,2177],{"class":75,"line":1713},[73,2178,222],{"emptyLinePlaceholder":221},[73,2180,2181],{"class":75,"line":1745},[73,2182,2183],{"class":79},"-- Compose the pipeline:\n",[73,2185,2186,2188,2191,2193,2196,2199,2201,2204,2206,2209,2211,2214,2216,2219,2221,2224],{"class":75,"line":1752},[73,2187,93],{"class":92},[73,2189,2190],{"class":96}," lines ",[73,2192,100],{"class":92},[73,2194,2195],{"class":96}," {",[73,2197,2198],{"class":124},"\"10\"",[73,2200,35],{"class":96},[73,2202,2203],{"class":124},"\"hello\"",[73,2205,35],{"class":96},[73,2207,2208],{"class":124},"\"20\"",[73,2210,35],{"class":96},[73,2212,2213],{"class":124},"\"world\"",[73,2215,35],{"class":96},[73,2217,2218],{"class":124},"\"30\"",[73,2220,35],{"class":96},[73,2222,2223],{"class":124},"\"40\"",[73,2225,2226],{"class":96},"}\n",[73,2228,2229,2231,2234,2236,2238,2240,2243,2246,2248,2251,2254,2257,2259,2262,2265,2268],{"class":75,"line":1780},[73,2230,93],{"class":92},[73,2232,2233],{"class":96}," numbers ",[73,2235,100],{"class":92},[73,2237,1942],{"class":103},[73,2239,107],{"class":96},[73,2241,2242],{"class":103},"read_lines",[73,2244,2245],{"class":96},"(lines), ",[73,2247,110],{"class":92},[73,2249,2250],{"class":96},"(s) ",[73,2252,2253],{"class":92},"return",[73,2255,2256],{"class":103}," tonumber",[73,2258,2250],{"class":96},[73,2260,2261],{"class":92},"~=",[73,2263,2264],{"class":103}," nil",[73,2266,2267],{"class":92}," end",[73,2269,215],{"class":96},[73,2271,2272,2274,2277,2279,2281,2284,2286,2288,2290,2292,2294,2296,2299,2301],{"class":75,"line":1787},[73,2273,93],{"class":92},[73,2275,2276],{"class":96}," doubled ",[73,2278,100],{"class":92},[73,2280,2035],{"class":103},[73,2282,2283],{"class":96},"(numbers, ",[73,2285,110],{"class":92},[73,2287,2250],{"class":96},[73,2289,2253],{"class":92},[73,2291,2256],{"class":103},[73,2293,2250],{"class":96},[73,2295,541],{"class":92},[73,2297,2298],{"class":103}," 2",[73,2300,2267],{"class":92},[73,2302,215],{"class":96},[73,2304,2305,2307,2310,2312,2314],{"class":75,"line":1792},[73,2306,93],{"class":92},[73,2308,2309],{"class":96}," result ",[73,2311,100],{"class":92},[73,2313,2109],{"class":103},[73,2315,2316],{"class":96},"(doubled)\n",[73,2318,2319,2321,2323,2326,2329,2332,2335],{"class":75,"line":1798},[73,2320,234],{"class":103},[73,2322,107],{"class":96},[73,2324,2325],{"class":103},"table.concat",[73,2327,2328],{"class":96},"(result, ",[73,2330,2331],{"class":124},"\", \"",[73,2333,2334],{"class":96},"))  ",[73,2336,2337],{"class":79},"-- 20, 40, 60, 80\n",[73,2339,2340],{"class":75,"line":1804},[73,2341,2342],{"class":79},"-- Lazy: each value flows through the pipeline only when drain pulls it\n",[56,2344,2346],{"id":2345},"cooperative-task-scheduler","Cooperative Task Scheduler",[61,2348,2349,2910,2914],{"language":63},[65,2350,2352],{"className":67,"code":2351,"language":63,"meta":69,"style":69},"-- Simple cooperative scheduler: round-robin over coroutines\n-- Each task yields voluntarily; scheduler gives each a turn\n\nlocal Scheduler = {}\nScheduler.__index = Scheduler\n\nfunction Scheduler.new()\n  return setmetatable({tasks = {}, current = 1}, Scheduler)\nend\n\nfunction Scheduler:add(fn, name)\n  local co = coroutine.create(fn)\n  self.tasks[#self.tasks + 1] = {co = co, name = name or \"unnamed\"}\n  return self\nend\n\nfunction Scheduler:run()\n  while true do\n    local remaining = 0\n    for i, task in ipairs(self.tasks) do\n      if coroutine.status(task.co) ~= \"dead\" then\n        remaining = remaining + 1\n        local ok, err = coroutine.resume(task.co)\n        if not ok then\n          print(\"task \" .. task.name .. \" error: \" .. tostring(err))\n        end\n      end\n    end\n    if remaining == 0 then break end  -- all tasks done\n  end\nend\n\n-- Usage: two tasks that yield to each other\nlocal sched = Scheduler.new()\nsched:add(function()\n  for i = 1, 3 do\n    print(\"A:\" .. i)\n    coroutine.yield()  -- give up control, scheduler picks next task\n  end\nend, \"A\")\nsched:add(function()\n  for i = 1, 3 do\n    print(\"B:\" .. i)\n    coroutine.yield()\n  end\nend, \"B\")\nsched:run()\n-- Output (interleaved): A:1, B:1, A:2, B:2, A:3, B:3\n",[24,2353,2354,2359,2364,2368,2379,2393,2397,2412,2434,2438,2442,2457,2470,2521,2528,2532,2536,2549,2558,2569,2592,2614,2627,2645,2658,2691,2696,2700,2704,2727,2731,2735,2739,2744,2760,2775,2791,2806,2816,2820,2831,2845,2861,2874,2880,2884,2895,2905],{"__ignoreMap":69},[73,2355,2356],{"class":75,"line":76},[73,2357,2358],{"class":79},"-- Simple cooperative scheduler: round-robin over coroutines\n",[73,2360,2361],{"class":75,"line":83},[73,2362,2363],{"class":79},"-- Each task yields voluntarily; scheduler gives each a turn\n",[73,2365,2366],{"class":75,"line":89},[73,2367,222],{"emptyLinePlaceholder":221},[73,2369,2370,2372,2375,2377],{"class":75,"line":116},[73,2371,93],{"class":92},[73,2373,2374],{"class":96}," Scheduler ",[73,2376,100],{"class":92},[73,2378,2124],{"class":96},[73,2380,2381,2384,2387,2390],{"class":75,"line":134},[73,2382,2383],{"class":96},"Scheduler.",[73,2385,2386],{"class":845},"__index",[73,2388,2389],{"class":92}," =",[73,2391,2392],{"class":96}," Scheduler\n",[73,2394,2395],{"class":75,"line":151},[73,2396,222],{"emptyLinePlaceholder":221},[73,2398,2399,2401,2404,2407,2410],{"class":75,"line":166},[73,2400,110],{"class":92},[73,2402,2403],{"class":845}," Scheduler",[73,2405,2406],{"class":96},".",[73,2408,2409],{"class":845},"new",[73,2411,113],{"class":96},[73,2413,2414,2416,2419,2422,2424,2427,2429,2431],{"class":75,"line":181},[73,2415,200],{"class":92},[73,2417,2418],{"class":103}," setmetatable",[73,2420,2421],{"class":96},"({tasks ",[73,2423,100],{"class":92},[73,2425,2426],{"class":96}," {}, current ",[73,2428,100],{"class":92},[73,2430,711],{"class":103},[73,2432,2433],{"class":96},"}, Scheduler)\n",[73,2435,2436],{"class":75,"line":197},[73,2437,933],{"class":92},[73,2439,2440],{"class":75,"line":209},[73,2441,222],{"emptyLinePlaceholder":221},[73,2443,2444,2446,2448,2451,2454],{"class":75,"line":218},[73,2445,110],{"class":92},[73,2447,2403],{"class":845},[73,2449,2450],{"class":96},":",[73,2452,2453],{"class":845},"add",[73,2455,2456],{"class":96},"(fn, name)\n",[73,2458,2459,2461,2463,2465,2467],{"class":75,"line":225},[73,2460,496],{"class":92},[73,2462,97],{"class":96},[73,2464,100],{"class":92},[73,2466,104],{"class":103},[73,2468,2469],{"class":96},"(fn)\n",[73,2471,2472,2475,2477,2480,2483,2485,2488,2490,2492,2495,2497,2499,2501,2504,2506,2509,2511,2514,2516,2519],{"class":75,"line":231},[73,2473,2474],{"class":103},"  self",[73,2476,2406],{"class":96},[73,2478,2479],{"class":845},"tasks",[73,2481,2482],{"class":96},"[",[73,2484,2144],{"class":92},[73,2486,2487],{"class":103},"self",[73,2489,2406],{"class":96},[73,2491,2479],{"class":845},[73,2493,2494],{"class":92}," +",[73,2496,711],{"class":103},[73,2498,2154],{"class":96},[73,2500,100],{"class":92},[73,2502,2503],{"class":96}," {co ",[73,2505,100],{"class":92},[73,2507,2508],{"class":96}," co, name ",[73,2510,100],{"class":92},[73,2512,2513],{"class":96}," name ",[73,2515,1041],{"class":92},[73,2517,2518],{"class":124}," \"unnamed\"",[73,2520,2226],{"class":96},[73,2522,2523,2525],{"class":75,"line":248},[73,2524,200],{"class":92},[73,2526,2527],{"class":103}," self\n",[73,2529,2530],{"class":75,"line":253},[73,2531,933],{"class":92},[73,2533,2534],{"class":75,"line":259},[73,2535,222],{"emptyLinePlaceholder":221},[73,2537,2538,2540,2542,2544,2547],{"class":75,"line":265},[73,2539,110],{"class":92},[73,2541,2403],{"class":845},[73,2543,2450],{"class":96},[73,2545,2546],{"class":845},"run",[73,2548,113],{"class":96},[73,2550,2551,2554,2556],{"class":75,"line":284},[73,2552,2553],{"class":92},"  while",[73,2555,885],{"class":103},[73,2557,719],{"class":92},[73,2559,2560,2562,2565,2567],{"class":75,"line":295},[73,2561,1409],{"class":92},[73,2563,2564],{"class":96}," remaining ",[73,2566,100],{"class":92},[73,2568,1203],{"class":103},[73,2570,2571,2573,2576,2578,2580,2582,2584,2586,2588,2590],{"class":75,"line":310},[73,2572,1061],{"class":92},[73,2574,2575],{"class":96}," i, task ",[73,2577,1106],{"class":92},[73,2579,1445],{"class":103},[73,2581,107],{"class":96},[73,2583,2487],{"class":103},[73,2585,2406],{"class":96},[73,2587,2479],{"class":845},[73,2589,1120],{"class":96},[73,2591,1071],{"class":92},[73,2593,2594,2596,2599,2602,2605,2607,2609,2612],{"class":75,"line":315},[73,2595,1979],{"class":92},[73,2597,2598],{"class":103}," coroutine.status",[73,2600,2601],{"class":96},"(task.",[73,2603,2604],{"class":845},"co",[73,2606,1120],{"class":96},[73,2608,2261],{"class":92},[73,2610,2611],{"class":124}," \"dead\" ",[73,2613,1988],{"class":92},[73,2615,2616,2619,2621,2623,2625],{"class":75,"line":332},[73,2617,2618],{"class":96},"        remaining ",[73,2620,100],{"class":92},[73,2622,2564],{"class":96},[73,2624,510],{"class":92},[73,2626,1044],{"class":103},[73,2628,2629,2632,2635,2637,2639,2641,2643],{"class":75,"line":343},[73,2630,2631],{"class":92},"        local",[73,2633,2634],{"class":96}," ok, err ",[73,2636,100],{"class":92},[73,2638,275],{"class":103},[73,2640,2601],{"class":96},[73,2642,2604],{"class":845},[73,2644,215],{"class":96},[73,2646,2647,2650,2653,2656],{"class":75,"line":348},[73,2648,2649],{"class":92},"        if",[73,2651,2652],{"class":92}," not",[73,2654,2655],{"class":96}," ok ",[73,2657,1988],{"class":92},[73,2659,2660,2663,2665,2668,2671,2674,2677,2680,2683,2685,2688],{"class":75,"line":365},[73,2661,2662],{"class":103},"          print",[73,2664,107],{"class":96},[73,2666,2667],{"class":124},"\"task \" ",[73,2669,2670],{"class":92},"..",[73,2672,2673],{"class":96}," task.",[73,2675,2676],{"class":845},"name",[73,2678,2679],{"class":92}," ..",[73,2681,2682],{"class":124}," \" error: \" ",[73,2684,2670],{"class":92},[73,2686,2687],{"class":103}," tostring",[73,2689,2690],{"class":96},"(err))\n",[73,2692,2693],{"class":75,"line":376},[73,2694,2695],{"class":92},"        end\n",[73,2697,2698],{"class":75,"line":391},[73,2699,1470],{"class":92},[73,2701,2702],{"class":75,"line":396},[73,2703,921],{"class":92},[73,2705,2706,2709,2711,2714,2716,2719,2722,2724],{"class":75,"line":413},[73,2707,2708],{"class":92},"    if",[73,2710,2564],{"class":96},[73,2712,2713],{"class":92},"==",[73,2715,860],{"class":103},[73,2717,2718],{"class":92}," then",[73,2720,2721],{"class":92}," break",[73,2723,2267],{"class":92},[73,2725,2726],{"class":79},"  -- all tasks done\n",[73,2728,2729],{"class":75,"line":936},[73,2730,743],{"class":92},[73,2732,2733],{"class":75,"line":941},[73,2734,933],{"class":92},[73,2736,2737],{"class":75,"line":955},[73,2738,222],{"emptyLinePlaceholder":221},[73,2740,2741],{"class":75,"line":973},[73,2742,2743],{"class":79},"-- Usage: two tasks that yield to each other\n",[73,2745,2746,2748,2751,2753,2756,2758],{"class":75,"line":995},[73,2747,93],{"class":92},[73,2749,2750],{"class":96}," sched ",[73,2752,100],{"class":92},[73,2754,2755],{"class":96}," Scheduler.",[73,2757,2409],{"class":103},[73,2759,113],{"class":96},[73,2761,2762,2765,2767,2769,2771,2773],{"class":75,"line":1612},[73,2763,2764],{"class":845},"sched",[73,2766,2450],{"class":96},[73,2768,2453],{"class":103},[73,2770,107],{"class":96},[73,2772,110],{"class":92},[73,2774,113],{"class":96},[73,2776,2777,2779,2781,2783,2785,2787,2789],{"class":75,"line":1618},[73,2778,703],{"class":92},[73,2780,706],{"class":96},[73,2782,100],{"class":92},[73,2784,711],{"class":103},[73,2786,35],{"class":96},[73,2788,716],{"class":103},[73,2790,719],{"class":92},[73,2792,2793,2796,2798,2801,2803],{"class":75,"line":1624},[73,2794,2795],{"class":103},"    print",[73,2797,107],{"class":96},[73,2799,2800],{"class":124},"\"A:\" ",[73,2802,2670],{"class":92},[73,2804,2805],{"class":96}," i)\n",[73,2807,2808,2810,2813],{"class":75,"line":1630},[73,2809,724],{"class":103},[73,2811,2812],{"class":96},"()  ",[73,2814,2815],{"class":79},"-- give up control, scheduler picks next task\n",[73,2817,2818],{"class":75,"line":1649},[73,2819,743],{"class":92},[73,2821,2822,2824,2826,2829],{"class":75,"line":1682},[73,2823,212],{"class":92},[73,2825,35],{"class":96},[73,2827,2828],{"class":124},"\"A\"",[73,2830,215],{"class":96},[73,2832,2833,2835,2837,2839,2841,2843],{"class":75,"line":1702},[73,2834,2764],{"class":845},[73,2836,2450],{"class":96},[73,2838,2453],{"class":103},[73,2840,107],{"class":96},[73,2842,110],{"class":92},[73,2844,113],{"class":96},[73,2846,2847,2849,2851,2853,2855,2857,2859],{"class":75,"line":1708},[73,2848,703],{"class":92},[73,2850,706],{"class":96},[73,2852,100],{"class":92},[73,2854,711],{"class":103},[73,2856,35],{"class":96},[73,2858,716],{"class":103},[73,2860,719],{"class":92},[73,2862,2863,2865,2867,2870,2872],{"class":75,"line":1713},[73,2864,2795],{"class":103},[73,2866,107],{"class":96},[73,2868,2869],{"class":124},"\"B:\" ",[73,2871,2670],{"class":92},[73,2873,2805],{"class":96},[73,2875,2876,2878],{"class":75,"line":1745},[73,2877,724],{"class":103},[73,2879,113],{"class":96},[73,2881,2882],{"class":75,"line":1752},[73,2883,743],{"class":92},[73,2885,2886,2888,2890,2893],{"class":75,"line":1780},[73,2887,212],{"class":92},[73,2889,35],{"class":96},[73,2891,2892],{"class":124},"\"B\"",[73,2894,215],{"class":96},[73,2896,2897,2899,2901,2903],{"class":75,"line":1787},[73,2898,2764],{"class":845},[73,2900,2450],{"class":96},[73,2902,2546],{"class":103},[73,2904,113],{"class":96},[73,2906,2907],{"class":75,"line":1792},[73,2908,2909],{"class":79},"-- Output (interleaved): A:1, B:1, A:2, B:2, A:3, B:3\n",[56,2911,2913],{"id":2912},"async-like-pattern-no-callbacks","Async-like Pattern (No Callbacks)",[61,2915,2916,3377,3381],{"language":63},[65,2917,2919],{"className":67,"code":2918,"language":63,"meta":69,"style":69},"-- Simulate async I\u002FO with coroutines: yield while \"waiting\", resume when \"ready\"\n-- This is how Lua frameworks (e.g., OpenResty, Luvit) implement async without callbacks\n\nlocal event_loop = {tasks = {}, timers = {}}\n\nfunction event_loop.async(fn)\n  local co = coroutine.create(fn)\n  event_loop.tasks[#event_loop.tasks + 1] = co\nend\n\nfunction event_loop.sleep(seconds)  -- \"async sleep\" — yields, scheduler resumes later\n  local wake_time = os.clock() + seconds\n  coroutine.yield(function()  -- yield a RESUME CONDITION (function)\n    return os.clock() >= wake_time  -- scheduler calls this; true = ready to resume\n  end)\nend\n\nfunction event_loop.run()\n  while #event_loop.tasks > 0 do\n    local pending = {}\n    for _, co in ipairs(event_loop.tasks) do\n      if coroutine.status(co) ~= \"dead\" then\n        local condition = coroutine.resume(co)\n        -- if the coroutine yielded a condition function, check it\n        if type(condition) == \"function\" and condition() then\n          coroutine.resume(co)  -- condition met, resume\n        elseif coroutine.status(co) ~= \"dead\" then\n          pending[#pending + 1] = co\n        end\n      end\n    end\n    event_loop.tasks = pending\n  end\nend\n\n-- Usage: \"async\" code that looks synchronous (no callbacks)\nevent_loop.async(function()\n  print(\"fetching...\")\n  event_loop.sleep(0.1)  -- \"await\" — yields, then resumes after 0.1s\n  print(\"done fetching\")\nend)\nevent_loop.run()\n-- This pattern scales to thousands of concurrent \"async\" tasks on one OS thread\n",[24,2920,2921,2926,2931,2935,2956,2960,2974,2986,3013,3017,3021,3038,3058,3071,3089,3095,3099,3103,3115,3132,3143,3163,3178,3192,3197,3222,3232,3247,3267,3271,3275,3279,3291,3295,3299,3303,3308,3320,3331,3347,3358,3364,3372],{"__ignoreMap":69},[73,2922,2923],{"class":75,"line":76},[73,2924,2925],{"class":79},"-- Simulate async I\u002FO with coroutines: yield while \"waiting\", resume when \"ready\"\n",[73,2927,2928],{"class":75,"line":83},[73,2929,2930],{"class":79},"-- This is how Lua frameworks (e.g., OpenResty, Luvit) implement async without callbacks\n",[73,2932,2933],{"class":75,"line":89},[73,2934,222],{"emptyLinePlaceholder":221},[73,2936,2937,2939,2942,2944,2947,2949,2952,2954],{"class":75,"line":116},[73,2938,93],{"class":92},[73,2940,2941],{"class":96}," event_loop ",[73,2943,100],{"class":92},[73,2945,2946],{"class":96}," {tasks ",[73,2948,100],{"class":92},[73,2950,2951],{"class":96}," {}, timers ",[73,2953,100],{"class":92},[73,2955,1372],{"class":96},[73,2957,2958],{"class":75,"line":134},[73,2959,222],{"emptyLinePlaceholder":221},[73,2961,2962,2964,2967,2969,2972],{"class":75,"line":151},[73,2963,110],{"class":92},[73,2965,2966],{"class":845}," event_loop",[73,2968,2406],{"class":96},[73,2970,2971],{"class":845},"async",[73,2973,2469],{"class":96},[73,2975,2976,2978,2980,2982,2984],{"class":75,"line":166},[73,2977,496],{"class":92},[73,2979,97],{"class":96},[73,2981,100],{"class":92},[73,2983,104],{"class":103},[73,2985,2469],{"class":96},[73,2987,2988,2991,2993,2995,2997,3000,3002,3004,3006,3008,3010],{"class":75,"line":181},[73,2989,2990],{"class":96},"  event_loop.",[73,2992,2479],{"class":845},[73,2994,2482],{"class":96},[73,2996,2144],{"class":92},[73,2998,2999],{"class":96},"event_loop.",[73,3001,2479],{"class":845},[73,3003,2494],{"class":92},[73,3005,711],{"class":103},[73,3007,2154],{"class":96},[73,3009,100],{"class":92},[73,3011,3012],{"class":96}," co\n",[73,3014,3015],{"class":75,"line":197},[73,3016,933],{"class":92},[73,3018,3019],{"class":75,"line":209},[73,3020,222],{"emptyLinePlaceholder":221},[73,3022,3023,3025,3027,3029,3032,3035],{"class":75,"line":218},[73,3024,110],{"class":92},[73,3026,2966],{"class":845},[73,3028,2406],{"class":96},[73,3030,3031],{"class":845},"sleep",[73,3033,3034],{"class":96},"(seconds)  ",[73,3036,3037],{"class":79},"-- \"async sleep\" — yields, scheduler resumes later\n",[73,3039,3040,3042,3045,3047,3050,3053,3055],{"class":75,"line":225},[73,3041,496],{"class":92},[73,3043,3044],{"class":96}," wake_time ",[73,3046,100],{"class":92},[73,3048,3049],{"class":103}," os.clock",[73,3051,3052],{"class":96},"() ",[73,3054,510],{"class":92},[73,3056,3057],{"class":96}," seconds\n",[73,3059,3060,3062,3064,3066,3068],{"class":75,"line":231},[73,3061,137],{"class":103},[73,3063,107],{"class":96},[73,3065,110],{"class":92},[73,3067,2812],{"class":96},[73,3069,3070],{"class":79},"-- yield a RESUME CONDITION (function)\n",[73,3072,3073,3076,3078,3080,3083,3086],{"class":75,"line":248},[73,3074,3075],{"class":92},"    return",[73,3077,3049],{"class":103},[73,3079,3052],{"class":96},[73,3081,3082],{"class":92},">=",[73,3084,3085],{"class":96}," wake_time  ",[73,3087,3088],{"class":79},"-- scheduler calls this; true = ready to resume\n",[73,3090,3091,3093],{"class":75,"line":253},[73,3092,926],{"class":92},[73,3094,215],{"class":96},[73,3096,3097],{"class":75,"line":259},[73,3098,933],{"class":92},[73,3100,3101],{"class":75,"line":265},[73,3102,222],{"emptyLinePlaceholder":221},[73,3104,3105,3107,3109,3111,3113],{"class":75,"line":284},[73,3106,110],{"class":92},[73,3108,2966],{"class":845},[73,3110,2406],{"class":96},[73,3112,2546],{"class":845},[73,3114,113],{"class":96},[73,3116,3117,3119,3121,3123,3125,3128,3130],{"class":75,"line":295},[73,3118,2553],{"class":92},[73,3120,1883],{"class":92},[73,3122,2999],{"class":96},[73,3124,2479],{"class":845},[73,3126,3127],{"class":92}," >",[73,3129,860],{"class":103},[73,3131,719],{"class":92},[73,3133,3134,3136,3139,3141],{"class":75,"line":310},[73,3135,1409],{"class":92},[73,3137,3138],{"class":96}," pending ",[73,3140,100],{"class":92},[73,3142,2124],{"class":96},[73,3144,3145,3147,3150,3152,3154,3157,3159,3161],{"class":75,"line":315},[73,3146,1061],{"class":92},[73,3148,3149],{"class":96}," _, co ",[73,3151,1106],{"class":92},[73,3153,1445],{"class":103},[73,3155,3156],{"class":96},"(event_loop.",[73,3158,2479],{"class":845},[73,3160,1120],{"class":96},[73,3162,1071],{"class":92},[73,3164,3165,3167,3169,3172,3174,3176],{"class":75,"line":332},[73,3166,1979],{"class":92},[73,3168,2598],{"class":103},[73,3170,3171],{"class":96},"(co) ",[73,3173,2261],{"class":92},[73,3175,2611],{"class":124},[73,3177,1988],{"class":92},[73,3179,3180,3182,3185,3187,3189],{"class":75,"line":343},[73,3181,2631],{"class":92},[73,3183,3184],{"class":96}," condition ",[73,3186,100],{"class":92},[73,3188,275],{"class":103},[73,3190,3191],{"class":96},"(co)\n",[73,3193,3194],{"class":75,"line":348},[73,3195,3196],{"class":79},"        -- if the coroutine yielded a condition function, check it\n",[73,3198,3199,3201,3204,3207,3209,3212,3215,3218,3220],{"class":75,"line":365},[73,3200,2649],{"class":92},[73,3202,3203],{"class":103}," type",[73,3205,3206],{"class":96},"(condition) ",[73,3208,2713],{"class":92},[73,3210,3211],{"class":124}," \"function\" ",[73,3213,3214],{"class":92},"and",[73,3216,3217],{"class":103}," condition",[73,3219,3052],{"class":96},[73,3221,1988],{"class":92},[73,3223,3224,3227,3229],{"class":75,"line":376},[73,3225,3226],{"class":103},"          coroutine.resume",[73,3228,278],{"class":96},[73,3230,3231],{"class":79},"-- condition met, resume\n",[73,3233,3234,3237,3239,3241,3243,3245],{"class":75,"line":391},[73,3235,3236],{"class":92},"        elseif",[73,3238,2598],{"class":103},[73,3240,3171],{"class":96},[73,3242,2261],{"class":92},[73,3244,2611],{"class":124},[73,3246,1988],{"class":92},[73,3248,3249,3252,3254,3257,3259,3261,3263,3265],{"class":75,"line":396},[73,3250,3251],{"class":96},"          pending[",[73,3253,2144],{"class":92},[73,3255,3256],{"class":96},"pending ",[73,3258,510],{"class":92},[73,3260,711],{"class":103},[73,3262,2154],{"class":96},[73,3264,100],{"class":92},[73,3266,3012],{"class":96},[73,3268,3269],{"class":75,"line":413},[73,3270,2695],{"class":92},[73,3272,3273],{"class":75,"line":936},[73,3274,1470],{"class":92},[73,3276,3277],{"class":75,"line":941},[73,3278,921],{"class":92},[73,3280,3281,3284,3286,3288],{"class":75,"line":955},[73,3282,3283],{"class":96},"    event_loop.",[73,3285,2479],{"class":845},[73,3287,2389],{"class":92},[73,3289,3290],{"class":96}," pending\n",[73,3292,3293],{"class":75,"line":973},[73,3294,743],{"class":92},[73,3296,3297],{"class":75,"line":995},[73,3298,933],{"class":92},[73,3300,3301],{"class":75,"line":1612},[73,3302,222],{"emptyLinePlaceholder":221},[73,3304,3305],{"class":75,"line":1618},[73,3306,3307],{"class":79},"-- Usage: \"async\" code that looks synchronous (no callbacks)\n",[73,3309,3310,3312,3314,3316,3318],{"class":75,"line":1624},[73,3311,2999],{"class":96},[73,3313,2971],{"class":103},[73,3315,107],{"class":96},[73,3317,110],{"class":92},[73,3319,113],{"class":96},[73,3321,3322,3324,3326,3329],{"class":75,"line":1630},[73,3323,119],{"class":103},[73,3325,107],{"class":96},[73,3327,3328],{"class":124},"\"fetching...\"",[73,3330,215],{"class":96},[73,3332,3333,3335,3337,3339,3342,3344],{"class":75,"line":1649},[73,3334,2990],{"class":96},[73,3336,3031],{"class":103},[73,3338,107],{"class":96},[73,3340,3341],{"class":103},"0.1",[73,3343,735],{"class":96},[73,3345,3346],{"class":79},"-- \"await\" — yields, then resumes after 0.1s\n",[73,3348,3349,3351,3353,3356],{"class":75,"line":1682},[73,3350,119],{"class":103},[73,3352,107],{"class":96},[73,3354,3355],{"class":124},"\"done fetching\"",[73,3357,215],{"class":96},[73,3359,3360,3362],{"class":75,"line":1702},[73,3361,212],{"class":92},[73,3363,215],{"class":96},[73,3365,3366,3368,3370],{"class":75,"line":1708},[73,3367,2999],{"class":96},[73,3369,2546],{"class":103},[73,3371,113],{"class":96},[73,3373,3374],{"class":75,"line":1713},[73,3375,3376],{"class":79},"-- This pattern scales to thousands of concurrent \"async\" tasks on one OS thread\n",[56,3378,3380],{"id":3379},"error-handling-in-coroutines","Error Handling in Coroutines",[61,3382,3383,3730,3736],{"language":63},[65,3384,3386],{"className":67,"code":3385,"language":63,"meta":69,"style":69},"-- coroutine.resume catches errors: returns false + error message (doesn't propagate)\n-- coroutine.wrap does NOT catch: errors propagate to the caller\n\nlocal co = coroutine.create(function()\n  error(\"boom inside coroutine\")\nend)\nlocal ok, err = coroutine.resume(co)\nprint(ok, err)  -- false  \"stdin:LINE: boom inside coroutine\" (caught, not propagated)\n\n-- wrap: error propagates (must pcall the wrapped function)\nlocal gen = coroutine.wrap(function()\n  error(\"boom from wrap\")\nend)\nlocal ok2, err2 = pcall(gen)  -- must pcall to catch\nprint(ok2, err2)  -- false  \"stdin:LINE: boom from wrap\"\n\n-- Production: wrap with error-safe generator\nlocal function safe_generator(fn)\n  local co = coroutine.create(fn)\n  return function()\n    local ok, result = coroutine.resume(co)\n    if not ok then return nil, result end  -- nil + error\n    return result  -- yield value or nil (dead)\n  end\nend\n\nlocal gen = safe_generator(function()\n  coroutine.yield(1)\n  coroutine.yield(2)\n  error(\"mid-generation failure\")\nend)\nprint(gen())  -- 1\nprint(gen())  -- 2\nlocal val, err = gen()\nprint(val, err)  -- nil  \"mid-generation failure\" (error caught, no propagation)\n",[24,3387,3388,3393,3398,3402,3418,3430,3436,3448,3458,3462,3467,3483,3494,3500,3518,3528,3532,3537,3548,3560,3568,3581,3605,3615,3619,3623,3627,3643,3653,3663,3674,3680,3693,3706,3720],{"__ignoreMap":69},[73,3389,3390],{"class":75,"line":76},[73,3391,3392],{"class":79},"-- coroutine.resume catches errors: returns false + error message (doesn't propagate)\n",[73,3394,3395],{"class":75,"line":83},[73,3396,3397],{"class":79},"-- coroutine.wrap does NOT catch: errors propagate to the caller\n",[73,3399,3400],{"class":75,"line":89},[73,3401,222],{"emptyLinePlaceholder":221},[73,3403,3404,3406,3408,3410,3412,3414,3416],{"class":75,"line":116},[73,3405,93],{"class":92},[73,3407,97],{"class":96},[73,3409,100],{"class":92},[73,3411,104],{"class":103},[73,3413,107],{"class":96},[73,3415,110],{"class":92},[73,3417,113],{"class":96},[73,3419,3420,3423,3425,3428],{"class":75,"line":134},[73,3421,3422],{"class":103},"  error",[73,3424,107],{"class":96},[73,3426,3427],{"class":124},"\"boom inside coroutine\"",[73,3429,215],{"class":96},[73,3431,3432,3434],{"class":75,"line":151},[73,3433,212],{"class":92},[73,3435,215],{"class":96},[73,3437,3438,3440,3442,3444,3446],{"class":75,"line":166},[73,3439,93],{"class":92},[73,3441,2634],{"class":96},[73,3443,100],{"class":92},[73,3445,275],{"class":103},[73,3447,3191],{"class":96},[73,3449,3450,3452,3455],{"class":75,"line":181},[73,3451,234],{"class":103},[73,3453,3454],{"class":96},"(ok, err)  ",[73,3456,3457],{"class":79},"-- false  \"stdin:LINE: boom inside coroutine\" (caught, not propagated)\n",[73,3459,3460],{"class":75,"line":197},[73,3461,222],{"emptyLinePlaceholder":221},[73,3463,3464],{"class":75,"line":209},[73,3465,3466],{"class":79},"-- wrap: error propagates (must pcall the wrapped function)\n",[73,3468,3469,3471,3473,3475,3477,3479,3481],{"class":75,"line":218},[73,3470,93],{"class":92},[73,3472,687],{"class":96},[73,3474,100],{"class":92},[73,3476,692],{"class":103},[73,3478,107],{"class":96},[73,3480,110],{"class":92},[73,3482,113],{"class":96},[73,3484,3485,3487,3489,3492],{"class":75,"line":225},[73,3486,3422],{"class":103},[73,3488,107],{"class":96},[73,3490,3491],{"class":124},"\"boom from wrap\"",[73,3493,215],{"class":96},[73,3495,3496,3498],{"class":75,"line":231},[73,3497,212],{"class":92},[73,3499,215],{"class":96},[73,3501,3502,3504,3507,3509,3512,3515],{"class":75,"line":248},[73,3503,93],{"class":92},[73,3505,3506],{"class":96}," ok2, err2 ",[73,3508,100],{"class":92},[73,3510,3511],{"class":103}," pcall",[73,3513,3514],{"class":96},"(gen)  ",[73,3516,3517],{"class":79},"-- must pcall to catch\n",[73,3519,3520,3522,3525],{"class":75,"line":253},[73,3521,234],{"class":103},[73,3523,3524],{"class":96},"(ok2, err2)  ",[73,3526,3527],{"class":79},"-- false  \"stdin:LINE: boom from wrap\"\n",[73,3529,3530],{"class":75,"line":259},[73,3531,222],{"emptyLinePlaceholder":221},[73,3533,3534],{"class":75,"line":265},[73,3535,3536],{"class":79},"-- Production: wrap with error-safe generator\n",[73,3538,3539,3541,3543,3546],{"class":75,"line":284},[73,3540,93],{"class":92},[73,3542,842],{"class":92},[73,3544,3545],{"class":845}," safe_generator",[73,3547,2469],{"class":96},[73,3549,3550,3552,3554,3556,3558],{"class":75,"line":295},[73,3551,496],{"class":92},[73,3553,97],{"class":96},[73,3555,100],{"class":92},[73,3557,104],{"class":103},[73,3559,2469],{"class":96},[73,3561,3562,3564,3566],{"class":75,"line":310},[73,3563,200],{"class":92},[73,3565,842],{"class":92},[73,3567,113],{"class":96},[73,3569,3570,3572,3575,3577,3579],{"class":75,"line":315},[73,3571,1409],{"class":92},[73,3573,3574],{"class":96}," ok, result ",[73,3576,100],{"class":92},[73,3578,275],{"class":103},[73,3580,3191],{"class":96},[73,3582,3583,3585,3587,3589,3592,3595,3597,3600,3602],{"class":75,"line":332},[73,3584,2708],{"class":92},[73,3586,2652],{"class":92},[73,3588,2655],{"class":96},[73,3590,3591],{"class":92},"then",[73,3593,3594],{"class":92}," return",[73,3596,2264],{"class":103},[73,3598,3599],{"class":96},", result ",[73,3601,212],{"class":92},[73,3603,3604],{"class":79},"  -- nil + error\n",[73,3606,3607,3609,3612],{"class":75,"line":343},[73,3608,3075],{"class":92},[73,3610,3611],{"class":96}," result  ",[73,3613,3614],{"class":79},"-- yield value or nil (dead)\n",[73,3616,3617],{"class":75,"line":348},[73,3618,743],{"class":92},[73,3620,3621],{"class":75,"line":365},[73,3622,933],{"class":92},[73,3624,3625],{"class":75,"line":376},[73,3626,222],{"emptyLinePlaceholder":221},[73,3628,3629,3631,3633,3635,3637,3639,3641],{"class":75,"line":391},[73,3630,93],{"class":92},[73,3632,687],{"class":96},[73,3634,100],{"class":92},[73,3636,3545],{"class":103},[73,3638,107],{"class":96},[73,3640,110],{"class":92},[73,3642,113],{"class":96},[73,3644,3645,3647,3649,3651],{"class":75,"line":396},[73,3646,137],{"class":103},[73,3648,107],{"class":96},[73,3650,142],{"class":103},[73,3652,215],{"class":96},[73,3654,3655,3657,3659,3661],{"class":75,"line":413},[73,3656,137],{"class":103},[73,3658,107],{"class":96},[73,3660,173],{"class":103},[73,3662,215],{"class":96},[73,3664,3665,3667,3669,3672],{"class":75,"line":936},[73,3666,3422],{"class":103},[73,3668,107],{"class":96},[73,3670,3671],{"class":124},"\"mid-generation failure\"",[73,3673,215],{"class":96},[73,3675,3676,3678],{"class":75,"line":941},[73,3677,212],{"class":92},[73,3679,215],{"class":96},[73,3681,3682,3684,3686,3688,3690],{"class":75,"line":955},[73,3683,234],{"class":103},[73,3685,107],{"class":96},[73,3687,762],{"class":103},[73,3689,765],{"class":96},[73,3691,3692],{"class":79},"-- 1\n",[73,3694,3695,3697,3699,3701,3703],{"class":75,"line":973},[73,3696,234],{"class":103},[73,3698,107],{"class":96},[73,3700,762],{"class":103},[73,3702,765],{"class":96},[73,3704,3705],{"class":79},"-- 2\n",[73,3707,3708,3710,3713,3715,3718],{"class":75,"line":995},[73,3709,93],{"class":92},[73,3711,3712],{"class":96}," val, err ",[73,3714,100],{"class":92},[73,3716,3717],{"class":103}," gen",[73,3719,113],{"class":96},[73,3721,3722,3724,3727],{"class":75,"line":1612},[73,3723,234],{"class":103},[73,3725,3726],{"class":96},"(val, err)  ",[73,3728,3729],{"class":79},"-- nil  \"mid-generation failure\" (error caught, no propagation)\n",[56,3731,3733,3735],{"id":3732},"coroutineyield-from-deep-nesting-stackful",[24,3734,26],{}," from Deep Nesting (Stackful)",[61,3737,3738,3934,3938,3951,3957,3963],{"language":63},[65,3739,3741],{"className":67,"code":3740,"language":63,"meta":69,"style":69},"-- Lua coroutines are STACKFUL: you can yield from arbitrarily deep call stacks\n-- This is the key advantage over Python (which needs \"yield from\") and JS generators\n-- (which can't yield from regular function calls)\n\nlocal function inner()\n  coroutine.yield(\"from inner\")  -- yield 3 levels deep\nend\n\nlocal function middle()\n  inner()\n  coroutine.yield(\"from middle\")\nend\n\nlocal function outer()\n  middle()\n  coroutine.yield(\"from outer\")\nend\n\nlocal gen = coroutine.wrap(outer)\nprint(gen())  -- \"from inner\" (yield propagated through 2 stack frames)\nprint(gen())  -- \"from middle\"\nprint(gen())  -- \"from outer\"\nprint(gen())  -- nil (dead)\n",[24,3742,3743,3748,3753,3758,3762,3773,3787,3791,3795,3806,3813,3824,3828,3832,3843,3850,3861,3865,3869,3882,3895,3908,3921],{"__ignoreMap":69},[73,3744,3745],{"class":75,"line":76},[73,3746,3747],{"class":79},"-- Lua coroutines are STACKFUL: you can yield from arbitrarily deep call stacks\n",[73,3749,3750],{"class":75,"line":83},[73,3751,3752],{"class":79},"-- This is the key advantage over Python (which needs \"yield from\") and JS generators\n",[73,3754,3755],{"class":75,"line":89},[73,3756,3757],{"class":79},"-- (which can't yield from regular function calls)\n",[73,3759,3760],{"class":75,"line":116},[73,3761,222],{"emptyLinePlaceholder":221},[73,3763,3764,3766,3768,3771],{"class":75,"line":134},[73,3765,93],{"class":92},[73,3767,842],{"class":92},[73,3769,3770],{"class":845}," inner",[73,3772,113],{"class":96},[73,3774,3775,3777,3779,3782,3784],{"class":75,"line":151},[73,3776,137],{"class":103},[73,3778,107],{"class":96},[73,3780,3781],{"class":124},"\"from inner\"",[73,3783,735],{"class":96},[73,3785,3786],{"class":79},"-- yield 3 levels deep\n",[73,3788,3789],{"class":75,"line":166},[73,3790,933],{"class":92},[73,3792,3793],{"class":75,"line":181},[73,3794,222],{"emptyLinePlaceholder":221},[73,3796,3797,3799,3801,3804],{"class":75,"line":197},[73,3798,93],{"class":92},[73,3800,842],{"class":92},[73,3802,3803],{"class":845}," middle",[73,3805,113],{"class":96},[73,3807,3808,3811],{"class":75,"line":209},[73,3809,3810],{"class":103},"  inner",[73,3812,113],{"class":96},[73,3814,3815,3817,3819,3822],{"class":75,"line":218},[73,3816,137],{"class":103},[73,3818,107],{"class":96},[73,3820,3821],{"class":124},"\"from middle\"",[73,3823,215],{"class":96},[73,3825,3826],{"class":75,"line":225},[73,3827,933],{"class":92},[73,3829,3830],{"class":75,"line":231},[73,3831,222],{"emptyLinePlaceholder":221},[73,3833,3834,3836,3838,3841],{"class":75,"line":248},[73,3835,93],{"class":92},[73,3837,842],{"class":92},[73,3839,3840],{"class":845}," outer",[73,3842,113],{"class":96},[73,3844,3845,3848],{"class":75,"line":253},[73,3846,3847],{"class":103},"  middle",[73,3849,113],{"class":96},[73,3851,3852,3854,3856,3859],{"class":75,"line":259},[73,3853,137],{"class":103},[73,3855,107],{"class":96},[73,3857,3858],{"class":124},"\"from outer\"",[73,3860,215],{"class":96},[73,3862,3863],{"class":75,"line":265},[73,3864,933],{"class":92},[73,3866,3867],{"class":75,"line":284},[73,3868,222],{"emptyLinePlaceholder":221},[73,3870,3871,3873,3875,3877,3879],{"class":75,"line":295},[73,3872,93],{"class":92},[73,3874,687],{"class":96},[73,3876,100],{"class":92},[73,3878,692],{"class":103},[73,3880,3881],{"class":96},"(outer)\n",[73,3883,3884,3886,3888,3890,3892],{"class":75,"line":310},[73,3885,234],{"class":103},[73,3887,107],{"class":96},[73,3889,762],{"class":103},[73,3891,765],{"class":96},[73,3893,3894],{"class":79},"-- \"from inner\" (yield propagated through 2 stack frames)\n",[73,3896,3897,3899,3901,3903,3905],{"class":75,"line":315},[73,3898,234],{"class":103},[73,3900,107],{"class":96},[73,3902,762],{"class":103},[73,3904,765],{"class":96},[73,3906,3907],{"class":79},"-- \"from middle\"\n",[73,3909,3910,3912,3914,3916,3918],{"class":75,"line":332},[73,3911,234],{"class":103},[73,3913,107],{"class":96},[73,3915,762],{"class":103},[73,3917,765],{"class":96},[73,3919,3920],{"class":79},"-- \"from outer\"\n",[73,3922,3923,3925,3927,3929,3931],{"class":75,"line":343},[73,3924,234],{"class":103},[73,3926,107],{"class":96},[73,3928,762],{"class":103},[73,3930,765],{"class":96},[73,3932,3933],{"class":79},"-- nil (dead)\n",[56,3935,3937],{"id":3936},"tips-tricks","💡 Tips & Tricks",[15,3939,3940,3950],{},[19,3941,3942,3943,3945,3946,3949],{},"Use ",[24,3944,653],{}," for iterators, ",[24,3947,3948],{},"create+resume"," for error handling",": wrap is cleaner for for-loops; create lets you catch errors.",[15,3952,3953,3956],{},[19,3954,3955],{},"Coroutines are cheap",": Each coroutine has its own stack (starts small, grows as needed). You can have thousands of them.",[15,3958,3959,3962],{},[19,3960,3961],{},"Generators are lazy — use them for infinite sequences",": Only compute what the consumer asks for.",[61,3964,3965],{"language":63},[65,3966,3968],{"className":67,"code":3967,"language":63,"meta":69,"style":69},"-- Infinite prime generator (lazy)\nlocal function primes()\n  local found = {}\n  return coroutine.wrap(function()\n    coroutine.yield(2)  -- first prime\n    local candidate = 3\n    while true do\n      local is_prime = true\n      for _, p in ipairs(found) do\n        if p * p > candidate then break end  -- only check up to sqrt\n        if candidate % p == 0 then is_prime = false; break end\n      end\n      if is_prime then\n        found[#found + 1] = candidate\n        coroutine.yield(candidate)\n      end\n      candidate = candidate + 2  -- skip even numbers\n    end\n  end)\nend\n\nlocal p = primes()\nfor i = 1, 10 do io.write(p(), \" \") end  -- 2 3 5 7 11 13 17 19 23 29\n",[24,3969,3970,3975,3986,3997,4009,4022,4034,4042,4055,4071,4096,4129,4133,4141,4162,4169,4173,4189,4193,4199,4203,4207,4219],{"__ignoreMap":69},[73,3971,3972],{"class":75,"line":76},[73,3973,3974],{"class":79},"-- Infinite prime generator (lazy)\n",[73,3976,3977,3979,3981,3984],{"class":75,"line":83},[73,3978,93],{"class":92},[73,3980,842],{"class":92},[73,3982,3983],{"class":845}," primes",[73,3985,113],{"class":96},[73,3987,3988,3990,3993,3995],{"class":75,"line":89},[73,3989,496],{"class":92},[73,3991,3992],{"class":96}," found ",[73,3994,100],{"class":92},[73,3996,2124],{"class":96},[73,3998,3999,4001,4003,4005,4007],{"class":75,"line":116},[73,4000,200],{"class":92},[73,4002,692],{"class":103},[73,4004,107],{"class":96},[73,4006,110],{"class":92},[73,4008,113],{"class":96},[73,4010,4011,4013,4015,4017,4019],{"class":75,"line":134},[73,4012,724],{"class":103},[73,4014,107],{"class":96},[73,4016,173],{"class":103},[73,4018,735],{"class":96},[73,4020,4021],{"class":79},"-- first prime\n",[73,4023,4024,4026,4029,4031],{"class":75,"line":151},[73,4025,1409],{"class":92},[73,4027,4028],{"class":96}," candidate ",[73,4030,100],{"class":92},[73,4032,4033],{"class":103}," 3\n",[73,4035,4036,4038,4040],{"class":75,"line":166},[73,4037,882],{"class":92},[73,4039,885],{"class":103},[73,4041,719],{"class":92},[73,4043,4044,4047,4050,4052],{"class":75,"line":181},[73,4045,4046],{"class":92},"      local",[73,4048,4049],{"class":96}," is_prime ",[73,4051,100],{"class":92},[73,4053,4054],{"class":103}," true\n",[73,4056,4057,4059,4062,4064,4066,4069],{"class":75,"line":197},[73,4058,1437],{"class":92},[73,4060,4061],{"class":96}," _, p ",[73,4063,1106],{"class":92},[73,4065,1445],{"class":103},[73,4067,4068],{"class":96},"(found) ",[73,4070,1071],{"class":92},[73,4072,4073,4075,4078,4080,4082,4085,4087,4089,4091,4093],{"class":75,"line":209},[73,4074,2649],{"class":92},[73,4076,4077],{"class":96}," p ",[73,4079,541],{"class":92},[73,4081,4077],{"class":96},[73,4083,4084],{"class":92},">",[73,4086,4028],{"class":96},[73,4088,3591],{"class":92},[73,4090,2721],{"class":92},[73,4092,2267],{"class":92},[73,4094,4095],{"class":79},"  -- only check up to sqrt\n",[73,4097,4098,4100,4102,4105,4107,4109,4111,4113,4115,4117,4120,4123,4126],{"class":75,"line":218},[73,4099,2649],{"class":92},[73,4101,4028],{"class":96},[73,4103,4104],{"class":92},"%",[73,4106,4077],{"class":96},[73,4108,2713],{"class":92},[73,4110,860],{"class":103},[73,4112,2718],{"class":92},[73,4114,4049],{"class":96},[73,4116,100],{"class":92},[73,4118,4119],{"class":103}," false",[73,4121,4122],{"class":96},"; ",[73,4124,4125],{"class":92},"break",[73,4127,4128],{"class":92}," end\n",[73,4130,4131],{"class":75,"line":225},[73,4132,1470],{"class":92},[73,4134,4135,4137,4139],{"class":75,"line":231},[73,4136,1979],{"class":92},[73,4138,4049],{"class":96},[73,4140,1988],{"class":92},[73,4142,4143,4146,4148,4151,4153,4155,4157,4159],{"class":75,"line":248},[73,4144,4145],{"class":96},"        found[",[73,4147,2144],{"class":92},[73,4149,4150],{"class":96},"found ",[73,4152,510],{"class":92},[73,4154,711],{"class":103},[73,4156,2154],{"class":96},[73,4158,100],{"class":92},[73,4160,4161],{"class":96}," candidate\n",[73,4163,4164,4166],{"class":75,"line":253},[73,4165,1993],{"class":103},[73,4167,4168],{"class":96},"(candidate)\n",[73,4170,4171],{"class":75,"line":259},[73,4172,1470],{"class":92},[73,4174,4175,4178,4180,4182,4184,4186],{"class":75,"line":265},[73,4176,4177],{"class":96},"      candidate ",[73,4179,100],{"class":92},[73,4181,4028],{"class":96},[73,4183,510],{"class":92},[73,4185,2298],{"class":103},[73,4187,4188],{"class":79},"  -- skip even numbers\n",[73,4190,4191],{"class":75,"line":284},[73,4192,921],{"class":92},[73,4194,4195,4197],{"class":75,"line":295},[73,4196,926],{"class":92},[73,4198,215],{"class":96},[73,4200,4201],{"class":75,"line":310},[73,4202,933],{"class":92},[73,4204,4205],{"class":75,"line":315},[73,4206,222],{"emptyLinePlaceholder":221},[73,4208,4209,4211,4213,4215,4217],{"class":75,"line":332},[73,4210,93],{"class":92},[73,4212,4077],{"class":96},[73,4214,100],{"class":92},[73,4216,3983],{"class":103},[73,4218,113],{"class":96},[73,4220,4221,4223,4225,4227,4229,4231,4233,4235,4237,4239,4241,4243,4245,4247,4249],{"class":75,"line":343},[73,4222,958],{"class":92},[73,4224,706],{"class":96},[73,4226,100],{"class":92},[73,4228,711],{"class":103},[73,4230,35],{"class":96},[73,4232,612],{"class":103},[73,4234,1298],{"class":92},[73,4236,1730],{"class":103},[73,4238,107],{"class":96},[73,4240,15],{"class":103},[73,4242,984],{"class":96},[73,4244,987],{"class":124},[73,4246,1120],{"class":96},[73,4248,212],{"class":92},[73,4250,4251],{"class":79},"  -- 2 3 5 7 11 13 17 19 23 29\n",[15,4253,4254,4259,4260,4263],{},[19,4255,3942,4256,4258],{},[24,4257,239],{}," to check if a generator is exhausted",": ",[24,4261,4262],{},"\"dead\""," means it's done.",[56,4265,4267],{"id":4266},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,4269,4270,4273],{},[19,4271,4272],{},"Cannot yield across C call boundaries",": If a coroutine calls a C function that calls back into Lua, you cannot yield from that Lua. This is the \"yield across C boundary\" error.",[61,4275,4276],{"language":63},[65,4277,4279],{"className":67,"code":4278,"language":63,"meta":69,"style":69},"-- table.sort calls a C function; if your comparator yields, it errors\nlocal co = coroutine.create(function()\n  table.sort({3, 1, 2}, function(a, b)\n    coroutine.yield()  -- ERROR: attempt to yield across a C-call boundary\n    return a \u003C b\n  end)\nend)\ncoroutine.resume(co)  -- false, \"attempt to yield across a C-call boundary\"\n-- Lua 5.4+ relaxed this for some functions, but not all (e.g., pcall still blocks)\n",[24,4280,4281,4286,4302,4328,4337,4349,4355,4361,4370],{"__ignoreMap":69},[73,4282,4283],{"class":75,"line":76},[73,4284,4285],{"class":79},"-- table.sort calls a C function; if your comparator yields, it errors\n",[73,4287,4288,4290,4292,4294,4296,4298,4300],{"class":75,"line":83},[73,4289,93],{"class":92},[73,4291,97],{"class":96},[73,4293,100],{"class":92},[73,4295,104],{"class":103},[73,4297,107],{"class":96},[73,4299,110],{"class":92},[73,4301,113],{"class":96},[73,4303,4304,4307,4310,4312,4314,4316,4318,4320,4323,4325],{"class":75,"line":89},[73,4305,4306],{"class":103},"  table.sort",[73,4308,4309],{"class":96},"({",[73,4311,716],{"class":103},[73,4313,35],{"class":96},[73,4315,142],{"class":103},[73,4317,35],{"class":96},[73,4319,173],{"class":103},[73,4321,4322],{"class":96},"}, ",[73,4324,110],{"class":92},[73,4326,4327],{"class":96},"(a, b)\n",[73,4329,4330,4332,4334],{"class":75,"line":116},[73,4331,724],{"class":103},[73,4333,2812],{"class":96},[73,4335,4336],{"class":79},"-- ERROR: attempt to yield across a C-call boundary\n",[73,4338,4339,4341,4344,4346],{"class":75,"line":134},[73,4340,3075],{"class":92},[73,4342,4343],{"class":96}," a ",[73,4345,1880],{"class":92},[73,4347,4348],{"class":96}," b\n",[73,4350,4351,4353],{"class":75,"line":151},[73,4352,926],{"class":92},[73,4354,215],{"class":96},[73,4356,4357,4359],{"class":75,"line":166},[73,4358,212],{"class":92},[73,4360,215],{"class":96},[73,4362,4363,4365,4367],{"class":75,"line":181},[73,4364,30],{"class":103},[73,4366,278],{"class":96},[73,4368,4369],{"class":79},"-- false, \"attempt to yield across a C-call boundary\"\n",[73,4371,4372],{"class":75,"line":197},[73,4373,4374],{"class":79},"-- Lua 5.4+ relaxed this for some functions, but not all (e.g., pcall still blocks)\n",[15,4376,4377,4380],{},[19,4378,4379],{},"Cannot resume a running coroutine",": Resuming from within itself (directly or via a callback) errors.",[61,4382,4383],{"language":63},[65,4384,4386],{"className":67,"code":4385,"language":63,"meta":69,"style":69},"local co = coroutine.create(function()\n  coroutine.resume(co)  -- ERROR: cannot resume non-suspended coroutine\nend)\ncoroutine.resume(co)\n",[24,4387,4388,4404,4414,4420],{"__ignoreMap":69},[73,4389,4390,4392,4394,4396,4398,4400,4402],{"class":75,"line":76},[73,4391,93],{"class":92},[73,4393,97],{"class":96},[73,4395,100],{"class":92},[73,4397,104],{"class":103},[73,4399,107],{"class":96},[73,4401,110],{"class":92},[73,4403,113],{"class":96},[73,4405,4406,4409,4411],{"class":75,"line":83},[73,4407,4408],{"class":103},"  coroutine.resume",[73,4410,278],{"class":96},[73,4412,4413],{"class":79},"-- ERROR: cannot resume non-suspended coroutine\n",[73,4415,4416,4418],{"class":75,"line":89},[73,4417,212],{"class":92},[73,4419,215],{"class":96},[73,4421,4422,4424],{"class":75,"line":116},[73,4423,30],{"class":103},[73,4425,3191],{"class":96},[15,4427,4428,4433,4434,4437],{},[19,4429,4430,4432],{},[24,4431,653],{}," returns nothing (not nil) when dead",": Calling a dead wrapped coroutine returns no values. ",[24,4435,4436],{},"nil","-checking the result is the standard pattern, but technically it's \"no return values\", not \"returns nil\".",[15,4439,4440,4443,4444,4447],{},[19,4441,4442],{},"Coroutines are not OS threads",": They run on the same OS thread, one at a time. No parallelism (only concurrency). For parallelism, use ",[24,4445,4446],{},"lua-llthreads"," or C-level threads.",[56,4449,4451],{"id":4450},"spot-the-bug","🧠 Spot the Bug",[61,4453,4454,4612],{"language":63},[65,4455,4457],{"className":67,"code":4456,"language":63,"meta":69,"style":69},"local function countdown_gen(n)\n  return coroutine.wrap(function()\n    for i = n, 1, -1 do\n      coroutine.yield(i)\n    end\n  end)\nend\n\nlocal count = countdown_gen(3)\nlocal values = {}\nfor v in count do  -- use the generator as an iterator\n  values[#values + 1] = v\nend\nprint(table.concat(values, \",\"))\n",[24,4458,4459,4470,4482,4503,4509,4513,4519,4523,4527,4544,4555,4570,4591,4595],{"__ignoreMap":69},[73,4460,4461,4463,4465,4468],{"class":75,"line":76},[73,4462,93],{"class":92},[73,4464,842],{"class":92},[73,4466,4467],{"class":845}," countdown_gen",[73,4469,1417],{"class":96},[73,4471,4472,4474,4476,4478,4480],{"class":75,"line":83},[73,4473,200],{"class":92},[73,4475,692],{"class":103},[73,4477,107],{"class":96},[73,4479,110],{"class":92},[73,4481,113],{"class":96},[73,4483,4484,4486,4488,4490,4493,4495,4497,4499,4501],{"class":75,"line":89},[73,4485,1061],{"class":92},[73,4487,706],{"class":96},[73,4489,100],{"class":92},[73,4491,4492],{"class":96}," n, ",[73,4494,142],{"class":103},[73,4496,35],{"class":96},[73,4498,1157],{"class":92},[73,4500,142],{"class":103},[73,4502,719],{"class":92},[73,4504,4505,4507],{"class":75,"line":116},[73,4506,892],{"class":103},[73,4508,1078],{"class":96},[73,4510,4511],{"class":75,"line":134},[73,4512,921],{"class":92},[73,4514,4515,4517],{"class":75,"line":151},[73,4516,926],{"class":92},[73,4518,215],{"class":96},[73,4520,4521],{"class":75,"line":166},[73,4522,933],{"class":92},[73,4524,4525],{"class":75,"line":181},[73,4526,222],{"emptyLinePlaceholder":221},[73,4528,4529,4531,4534,4536,4538,4540,4542],{"class":75,"line":197},[73,4530,93],{"class":92},[73,4532,4533],{"class":96}," count ",[73,4535,100],{"class":92},[73,4537,4467],{"class":103},[73,4539,107],{"class":96},[73,4541,716],{"class":103},[73,4543,215],{"class":96},[73,4545,4546,4548,4551,4553],{"class":75,"line":209},[73,4547,93],{"class":92},[73,4549,4550],{"class":96}," values ",[73,4552,100],{"class":92},[73,4554,2124],{"class":96},[73,4556,4557,4559,4561,4563,4565,4567],{"class":75,"line":218},[73,4558,958],{"class":92},[73,4560,1718],{"class":96},[73,4562,1106],{"class":92},[73,4564,4533],{"class":96},[73,4566,1123],{"class":92},[73,4568,4569],{"class":79},"  -- use the generator as an iterator\n",[73,4571,4572,4575,4577,4580,4582,4584,4586,4588],{"class":75,"line":225},[73,4573,4574],{"class":96},"  values[",[73,4576,2144],{"class":92},[73,4578,4579],{"class":96},"values ",[73,4581,510],{"class":92},[73,4583,711],{"class":103},[73,4585,2154],{"class":96},[73,4587,100],{"class":92},[73,4589,4590],{"class":96}," v\n",[73,4592,4593],{"class":75,"line":231},[73,4594,933],{"class":92},[73,4596,4597,4599,4601,4603,4606,4609],{"class":75,"line":248},[73,4598,234],{"class":103},[73,4600,107],{"class":96},[73,4602,2325],{"class":103},[73,4604,4605],{"class":96},"(values, ",[73,4607,4608],{"class":124},"\",\"",[73,4610,4611],{"class":96},"))\n",[4613,4614,4615,4619,4625,4647],"details",{},[4616,4617,4618],"summary",{},"Answer",[15,4620,4621,4622,2406],{},"Prints ",[24,4623,4624],{},"3,2,1",[15,4626,4627,4628,4630,4631,4633,4634,4636,4637,4640,4641,4643,4644,4646],{},"The generator function (from ",[24,4629,653],{},") is a valid iterator for the generic ",[24,4632,958],{}," loop. The ",[24,4635,958],{}," loop calls ",[24,4638,4639],{},"count()"," repeatedly until it returns ",[24,4642,4436],{}," (or no values, which is treated as nil). The loop gets 3, 2, 1, then the coroutine dies (the for loop in the generator completes), ",[24,4645,4639],{}," returns nothing → loop ends.",[15,4648,4649,4650,4652],{},"The bug would be if the generator yielded ",[24,4651,4436],{}," intentionally — the for loop would terminate early. For generators that might yield nil, use a sentinel value or a different protocol.",[4654,4655,4656],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}",{"title":69,"searchDepth":83,"depth":83,"links":4658},[4659,4660,4661,4662],{"id":58,"depth":83,"text":59},{"id":424,"depth":83,"text":425},{"id":4266,"depth":83,"text":4267},{"id":4450,"depth":83,"text":4451},"[object Object]","md",{},"\u002Flua\u002F11-coroutines",{"title":5,"description":4668},{"Deep-dive into Lua coroutines":4669},"coroutine.create\u002Fresume\u002Fyield mechanics, the yield-resume value passing protocol, generators via coroutine.wrap, cooperative task scheduling, producer-consumer pipelines, and async-like patterns without external libraries. Production patterns for iterators, cooperative scheduling, and backpressure.","lua\u002F11-coroutines","lUQESa6vof-dKUdqF93rcXvhikdsLNl5T9ng7QPeq0E",1789924650562]