Synchronous API design

70 views
Skip to first unread message

Connor McKay

unread,
Feb 12, 2015, 3:50:17 AM2/12/15
to lu...@googlegroups.com
I discovered Luvit about a month ago, and have become very excited about the possibilities it holds. I had never used Lua before, but I am quickly falling in love with its clean, minimalist design (what Javascript could/should have been), and its coroutines. I've read a few posts on this list discussing alternate ideas for an API that would break away from the callback-hell of Node, and I wanted to see if it was possible to create an API that would perfectly mimic synchronous operations, while still allowing asynchronous parallelism and background work. Below is a minimal example (that doesn't actually use Luvit itself):

callbacks = {}

function event_loop()
 
while (#callbacks > 0) do
    time
= os.time()
   
for i, v in ipairs(callbacks) do
     
if time >= v[1] then
        table
.remove(callbacks, i)
        v
[2]()
       
break
     
end
   
end
 
end
end

-- Callback style functions

function set_timeout(seconds, cb)
  table
.insert(callbacks, {os.time() + seconds, cb})
end

function load_name(cb)
  set_timeout
(3, function() cb("Bob") end)
end

-- Synchronous style functions

function pause(seconds)
 
local co = coroutine.running()
  set_timeout
(seconds, function()
   
assert(coroutine.resume(co))
 
end)
  coroutine
.yield()
end

function get_name()
 
local co = coroutine.running()
  load_name
(function(name)
   
assert(coroutine.resume(co, name))
 
end)
 
return coroutine.yield()
end

-- Parallelization functions

function spawn(func)
  coroutine
.wrap(func)()
end

function parallel(...)
 
local args = {...}
 
local done = 0
 
local co = coroutine.running()
 
function task_done()
   
done = done + 1
   
if done == #args then
     
assert(coroutine.resume(co))
   
end
 
end
 
for i, func in ipairs(args) do
    coroutine
.wrap(function()
      func
()
      task_done
()
   
end)()
 
end
  coroutine
.yield()
end

-- The main program itself

function main()
 
print("Hello main, pause 2")
  pause
(2)

 
print("Getting name...")
 
local name = get_name()
 
print("Name was " .. name)

  parallel
(function()
   
print("Parallel 1, pause 3")
    pause
(3)
   
print("Done parallel 1")
 
end,
 
function()
   
print("Parallel 2, pause 3")
    pause
(3)
   
print("Done parallel 2")
 
end)

 
print("Done parallel, in main")

  spawn
(function()
   
print("In spawn 1, pause for 5 seconds")
    pause
(5)
   
print("Done with spawn 1")
 
end)

  spawn
(function()
   
print("In spawn 2, pause for 5 seconds")
    pause
(5)
   
print("Done with spawn 2")
 
end)

 
print("Main pause 2")
  pause
(2)

 
print("Done main")
end

coroutine
.wrap(main)()

event_loop
()

This is little more than a proof of concept at the moment, but I thought it might be useful in the API design discussion.

Thanks,
Connor McKay

Tim Caswell

unread,
Feb 12, 2015, 7:16:12 AM2/12/15
to lu...@googlegroups.com

Yep, this is basically the style I'm using in luvit's new package manager (luvit/lit).  It's gets a little tricky around error handling, but most that can be handled in the spawn and parallel helpers.

--
You received this message because you are subscribed to the Google Groups "luvit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to luvit+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Connor McKay

unread,
Feb 12, 2015, 9:42:05 AM2/12/15
to lu...@googlegroups.com
That's great to hear! Is your plan to use this style as the default in luvit, or leave it up to individual apps/plugins to wrap the callback style functions into coroutine style?

You received this message because you are subscribed to a topic in the Google Groups "luvit" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/luvit/625We87Q5co/unsubscribe.
To unsubscribe from this group and all its topics, send an email to luvit+un...@googlegroups.com.

Tim Caswell

unread,
Feb 13, 2015, 8:19:03 AM2/13/15
to lu...@googlegroups.com
Luvit 2.0 will be a set of libraries that you can choose to use in your app.  It will use callback style primarily since the goal of the luvit api is to mimic node.js.  I did find a backwards compatible way to also support coroutines that I plan to integrate all over luvit.  Basically if you pass in the current coroutine `coroutine.running()` in place of the callback, it will suspend and return the result.

The core framework is API style agnostic.  I'm using coroutine style exclusively in in the lit project.  My idea is that new applications will be able to choose the style they want from the publis repository of packages.

-Tim Caswell
Reply all
Reply to author
Forward
0 new messages