lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Right, the one reason we need coroutines is to reimplement the standard library. Just as a warning, when asked to produce a license, this message was said:

> <Soni> TsT: if you wanna use it, contact me

So... Here's some code that's pretty useless that you need the author's permission to use.

On Fri, Feb 24, 2017, 09:17 Soni L. <> wrote:
Original post:
(short link:

This version is tweaked to be more complete (includes assert and xpcall).

# Why you need coroutines: pcall and error without pcall and error.

So, let's take Lua, remove pcall and error, and tweak coroutines a bit:


-- get rid of native pcall and error (and stuff)
pcall = nil
error = nil
xpcall = nil
assert = nil

-- we're gonna wrap coroutines, so this is important
local cocreate, coresume, costatus, corunning = coroutine.create,
coroutine.resume, coroutine.status, coroutine.running
local isdead = {}
setmetatable(isdead, {__mode="k"})

-- reimplement coroutine.resume
coroutine.resume = function(...)
   return (function(ok, ...) if ok then return ... else return ok, ...
end end)(coresume(...))

-- reimplement coroutine.yield
coroutine.yield = function(...)
   return coyield(true, ...)

-- reimplement coroutine.status
coroutine.status = function(co)
   if isdead[co] == true then return "dead" else return costatus(co) end

-- reimplement coroutine.wrap
coroutine.wrap = function(f)
   local co = cocreate(f)
   return function(...)
     return (function(...)
       if ... then
         return select(2, ...)
         -- note: using global. will be redefined below.
         error((select(2, ...)))
     end)(coroutine.resume(co)) -- note that we replaced
coroutine.resume above

-- reimplement error on top of coroutines
error = function(msg, level)
   local f = isdead[corunning()] -- ew, but eh it works \o/
   isdead[corunning()] = true
   if f then
     if not pcall(f, msg) then -- uses our own pcall
       level = 0
       msg = "error in message handler"
   coyield(false, msg, level or 1) -- uses raw/internal coyield, not the
wrapper we created above
   while true do coyield(false, "attempt to resume a dead coroutine") end

-- reimplement pcall on top of (our) xpcall
pcall = function(f, ...)
   return xpcall(f, nil, ...)

-- reimplement xpcall on top of coroutines
xpcall = function(f, msgh, ...)
   local co = cocreate(f)
   assert(msgh ~= true, "bad argument #2 (function expected, got
boolean)") -- lol
   isdead[co] = msgh
   local function recurse2(recurse, ...)
     return recurse(coroutine.resume(co, ...))
   local function recurse(ok, ...)
     if coroutine.status(co) == "dead" then
       return ok, ...
       return recurse2(recurse, coroutine.yield(...))
   return recurse2(recurse, ...)

-- assert is a simple wrapper around error
assert = function(...)
   local ok, err = ...
   if not ok then error(err or "assertion failed") end
   return ...

And now we have pcall and error implemented on top of coroutines (a
"high-level" pcall and error).

Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


Ryan <>
Software Developer / System Administrator