As a newcomer, I decided to try and write a small actor library as an excuse to learn Nim. It does need more work, especially on the scheduler, but I'd love feedback from the community. This is definitely something I might want to use in the future.

https://github.com/evacchi/nimoy

Share your thoughts, feel free to PR.

2017-03-30 20:10:17
Nice! I'm curious how it performs, do you have any plans for creating some benchmarks? 2017-03-30 20:47:34

I think it's too early right now, it really is a handful of lines of code; but I probably will if there is enough interest.

RIght now, actors run on a thread pool (not Nim's threadpool) that can be configured.

The execution strategy and migration of an actor across threads is pretty dumb, but it's pluggable. Any contribution in this regard would be welcome.

My idea (not implemented right now) is to keep actors on the same thread unless some condition is not met (to limit copying). Messaging uses channels.

2017-03-31 07:45:28
That's great, I hope this becomes a viable alternative to handle multithreaded applications in Nim!
2017-03-31 08:07:59
Very interested to see where this goes. Basically what andrea and dom said, once it's matured a bit. 2017-03-31 13:27:08
news: an improved (i.e., slightly less dumb) executor strategy, and a few new examples, such as an async, multithreaded "loader"
2017-04-07 10:13:55

news: featuring super-experimental support for actor topologies

import future, nimoy, nimoy/topologies

#
# source ~> map1 ~> fanIn ~> map3 ~> broadcast ~> sink1
#    +~~~~> map2 ~~~~~^                 +~~~~> sink2
#

let t = createTopology()

let sink1Ref = t.sinkRef((x: int) => echo("sink1 = ", x))
let sink2Ref = t.sinkRef((x: int) => echo("sink2 = ", x))
let bref     = t.broadcastRef(sink1Ref, sink2Ref)
let map3Ref  = t.nodeRef((x: int) => x+7,  bref)
let fanInRef = t.nodeRef((x: int) => ( echo("fan in = ", x); x ), map3Ref)
let map2Ref  = t.nodeRef((x: int) => x-1, fanInRef)
let map1Ref  = t.nodeRef((x: int) => x*2, fanInRef)

# send input
for i in 0..10:
  map1Ref ! i

t.awaitTermination()

2017-04-10 16:03:29

news: no changes on the surface, a few internal changes. New API: support for "actor channels" a thin wrapper around shared channels

let t = createTopology()
let sink1 = allocActorChannel[int]()
let sink2 = allocActorChannel[int]()

# declare output sinks
let sink1Ref = t.sinkRef((x: int) => sink1.send(x*100))
let sink2Ref = t.sinkRef((x: int) => sink2.send(x))

# broadcast to both sinks
let bref     = t.broadcastRef(sink1Ref, sink2Ref)

# map to the broadcast
let map3Ref  = t.nodeRef((x: int) => x+7,  bref)

# map to map3
let fanInRef = t.nodeRef((x: int) => ( echo("fan in = ", x); x ), map3Ref)

# two nodes that both go into fanIn
let map1Ref  = t.nodeRef((x: int) => x*2, fanInRef)
let map2Ref  = t.nodeRef((x: int) => x-1, fanInRef)

# send data to both the sources
for i in 0..10:
  map1Ref ! i
  map2Ref ! i

for i in 0..10:
  echo sink1.recv()
  echo sink2.recv()

2017-04-19 16:23:51