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