You don't need special syntax - Why you need coroutines: Try-catch in Lua.
local t =try(file(io.open("/dev/null", 'w')), function(devnull)
devnull:write("stuff")
return {}
end).catch("IOException", function(e)
-- ignore itend).finally()
The call to finally is required. When called, finally first calls the function from try in a coroutine, then if that coroutine yields an IOException in a specific way, it calls the function from catch, or if another type of exception is yielded, it yields to the parent try-catch, and so on. Either way, it calls finally after exiting the try, and automatically closes any associated resources (which need to be wrapped with file() or closeable() or some other wrapper, since you can't shove varargs/varrets in the middle of the args list and stuff).
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:
-- NOTE: THIS IMPLEMENTATION IS UNTESTED AND MAY CONTAIN BUGS.-- get rid of native pcall and error
pcall =nil
error =nil-- we're gonna wrap coroutines, so this is importantlocal cocreate, coresume, costatus, corunning = coroutine.create, coroutine.resume, coroutine.status, coroutine.runninglocal isdead = {}
setmetatable(isdead, {__mode="k"})
-- reimplement coroutine.resume
coroutine.resume=function(...)
return (function(ok, ...) if ok thenreturn...elsereturn ok, ...endend)(coresume(...))
end-- reimplement coroutine.yield
coroutine.yield=function(...)
returncoyield(true, ...)
end-- reimplement coroutine.status
coroutine.status=function(co)
if isdead[co] thenreturn"dead"elsereturncostatus(co) endend-- reimplement coroutine.wrap
coroutine.wrap=function(f)
local co =cocreate(f)
returnfunction(...)
return (function(...)
if...thenreturnselect(2, ...)
else-- note: using global. will be redefined below.error((select(2, ...)))
endend)(coroutine.resume(co)) -- note that we replaced coroutine.resume aboveendend-- reimplement error on top of coroutines
error =function(msg, level)
isdead[corunning()] =truecoyield(false, msg, level or1) -- uses raw/internal coyield, not the wrapper we created abovewhiletruedocoyield(false, "attempt to resume a dead coroutine") endend-- reimplement pcall on top of coroutines
pcall =function(f, ...)
local co =cocreate(f)
localfunctionrecurse2(recurse, ...)
returnrecurse(coroutine.resume(co, ...))
endlocalfunctionrecurse(ok, ...)
ifcoroutine.status(co) =="dead"thenreturn ok, ...elsereturnrecurse2(recurse, coroutine.yield(...))
endendreturnrecurse2(recurse, ...)
end-- xpcall is similar, except the error handler needs to be called by error().
And now we have pcall and error implemented on top of coroutines (a "high-level" pcall and error).