Ruby Polyglot: Talking with Erlang

Functional programming languages such as Erlang, Haskell, and Clojure have been gaining momentum over the last year and a half, promising us support for easy distributed, fault-tolerant, non-stop applications. Arguably the most appealing aspects are the high-concurrency actor and message passing models, especially in the light of imposed limits by the GIL on the Ruby interpreter (Concurrency is a Myth in Ruby). However, working my way through Joe Armstrong's "Programming Erlang", which is an excellent introductory book by the way, you quickly come to appreciate the lack of third party libraries that are available to any Ruby programmer.

Polyglot (person) (from the Greek πολύς /po΄lis/ and γλώττα /΄ɣlota/), someone who uses two or more languages

Erlectricity bridge: Connecting Ruby and Erlang

Turns out, Scott Fleckenstein and Tom Preston-Werner have already solved this problem for us, allowing bi-directional data exchange between Erlang and Ruby via Erlang Ports. As far as Erlang is concerned, this communication pattern is seen as direct communication with another Erlang process, which also means we can transparently invoke our Ruby interpreter whenever we need it! To get started, gem install erlectricity: Erlectricity allows a Ruby program to receive and respond to Erlang messages sent over the Erlang binary protocol.

Hello World from Erlang, through Ruby

Erlectricity source provides a couple of great examples of interfacing Ruby and Erlang: echo server, generating charts with Gruff, and even chatting with the Campfire API. In our case, we're going to connect Ruby and Erlang to create a "Hello World tweet". To do this, we're going to use John Nunemaker's twitter gem to avoid building an API client in Erlang:

require 'rubygems'
require 'erlectricity'
require 'stringio'
require 'twitter'

receive do |f|
  f.when([:post, String]) do |msg|

    # connect to twitter & post Hello World
    id = Twitter::Base.new('username', 'pass').post(msg).id
    f.send!([:result, "Tweet sent! Status id: #{id}"])

    f.receive_loop
  end
end

Our Ruby process will be invoked via Erlang and will process incoming messages by listening in the receive state. In this case, we will only pattern match on a 'post' request with String. Now the Erlang side:

-module(twitter).
-export([hello_world/0]).

hello_world() ->
  Cmd = "ruby twitter-erl.rb",
  Port = open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]),
  Payload = term_to_binary({post, <<"Hello World! (from Erlang, through Ruby)">>}),

 % send command to Ruby script & wait for response
  port_command(Port, Payload),

  receive
    {Port, {data, Data}} ->
      {result, Text} = binary_to_term(Data),

      % print out resulting tweet
      io:format("~p~n", [Text])
  end.
twitter-ruby-erl.zip - Full source, connecting Ruby and Erlang

The Erlang code is slightly longer, but very straightforward. We declare a hello_world function and open a byte-oriented port to the Ruby interpreter. From there, we send the command and block, waiting for the response. Now we can post our tweet:

Eshell V5.6.5 (abort with ^G)
1> c(twitter).
{ok,twitter}
2> twitter:hello_world().
<<"Tweet sent! Status id: 1358682092">>

Polyglot future: Imperative + Declarative programming

While a trivial one, this is an incredibly powerful example of polyglot programming. Languages such as Erlang, and functional / declarative programming in general, are definitely here to stay and will occupy an important architectural niche moving forward. However, one paradigm does not have to come at the cost of another, and in fact, it is most likely the combination of both OO and functional programming that will be the cornerstone of our future architectures and applications.


Ilya Grigorik

Ilya Grigorik is a web performance engineer and developer advocate at Google, where his focus is on making the web fast and driving adoption of performance best practices at Google and beyond.