DEV Community

Allain Lalonde
Allain Lalonde

Posted on

TartJS - Tiny Actor Run-Time

Introduction

While researching for a problem I was having at work, I dove headlong into a number of Node.js libraries that implement Actor Model Systems.

I stumbled from comedy to nact, and then tried writing a couple myself to refine my thinking on the Actor Model.

All left me feeling that the elegance of the Actor Model was being hidden behind layers of abstraction.

That is, until I stumbled upon a tiny JavaScript library called tartjs which seemed to expose the kernel of the idea.

TartJS

The following is a modification of their tweet example that exposes their idea perfectly. They have multiple versions exposed (Tweet, Minimal, and Pluggable) but the Tweet example demonstrates the idea best.:

function sponsor (behavior) {
  var ctx = {
    self,
    behavior,
    sponsor
  }
  function self (message) {
    process.nextTick(ctx.behavior.bind(ctx), message)
  }
  return self
}
Enter fullscreen mode Exit fullscreen mode

2 Functions, a way of delivering messages (process.nextTick call), and some context. That's it. No More.

Addresses

Unlike other actor model libraries tartjs uses functions as addresses, as opposed to Symbols, Objects, or strings. This function is also conveniently how you send messages to the actor for processing.

As an example:

const actor = sponsor(function helloBeh (name) {
  console.log('Hello', name)
})

actor('World')
Enter fullscreen mode Exit fullscreen mode

They've combined addresses and the sending of messages resulting in many actor model idioms being cleaner to write.

Here's a re-implementation of the above hello world example that uses the Customer/Service Idiom and reuses its created actors from call to call:


function buildHelloBeh () {
  var builder
  var logger
  return (name) {
    if (!builder) builder = this.sponsor(builderBeh)
    if (!logger) logger = this.sponsor(loggerBeh)
    // send a request to the builder actor, and tell it respond to the logger actor
    builder({ name, ok: logger })
  }
}

function builderBeh ({ name, ok }) {
  ok({ message: 'hello ' + name })
}

function loggerBeh ({ message }) {
  console.log(message)
}

const hello = sponsor(helloBeh)

hello('World')
Enter fullscreen mode Exit fullscreen mode

Note that the code above uses only JavaScript ideas that most developers are probably familiar with. How cool is that?

Mailboxes

Where other actor model libraries implement a mailbox that sits in front of an actor and queues up its messages for later processing, TartJS just delegates that to the JavaScript Event Loop. It gets out of JavaScript's way and reaps its benefits.

If you want something fancier, you can implement a mailbox actor that sends messages out according to any policy you want.

For example here's a simple mailbox that just forces messages to wait 1 second before being dispatched:

function buildSlowMailboxBeh (targetActor) {
  return function (msg, ctx) {
    setTimeout(() => {
      targetActor(msg)
    }, 1000)
  }
}

// the hello actor from before is made slow this way
const slowerHello = sponsor(buildSlowMailboxBeh(hello))

slowerHello('Hello')
Enter fullscreen mode Exit fullscreen mode

Conclusion

The TartJS git repo has not been touched since 2016, which is a shame because there's a kernel here that is uniquely elegant. With the smallest of overhead, and by trusting that JavaScript's environment can be good enough, it managed to point at something much bigger than its code.

The authors @dalnefre, and @tristanls are busy working in other languages it seems (go, rust). I wish them well, but my hope is that one day they return and continue to show this gem of a project some love.

Discussion (0)