Concurrency and expressiveness

Posted by Yariv on April 15, 2008

Damien Katz’s article Lisp as Blub has sparked a lively debate on Hacker News on the relative merits of Erlang and other languages for building robust applications. Good points were made on all sides (except for the tired complaints about Erlang syntax — I have a suspicion that most people who complain about it haven’t done much coding in Erlang). Unfortunately, I think that a key point was lost in all the noise: all else being equal, a language with great support for concurrency and fault tolerance has a higher expressive power than a language that doesn’t. It just lets you build concurrent applications much more easily (less code, fewer bugs, better scalability, yadda yadda).

In Response to “What Sucks About Erlang”

Posted by Yariv on March 09, 2008

Damien Katz’s latest blog post lists some ways in which Damien Katz thinks Erlang sucks. I agree with some of these points but not with all of them. Below are my responses to some of his complaints:

1. Basic Syntax

I’ve heard many people express their dislike for the Erlang syntax. I found the syntax a bit weird when I started using it, but once I got used to it it hasn’t bothered me much. Sometimes I mess up and use the wrong expression terminator, and sometimes things break when I cut and paste code between function clauses, but it hasn’t been real pain point for me. I understand where the complaints are coming from, but IMHO it’s a minor issue.

Since the release of LFE last week, if you don’t like the Erlang syntax, you can write Erlang code using Lisp Syntax, with full support for Lisp macros. If you prefer Lisp syntax to Erlang syntax, you have a choice.

2. ‘if’ expressions

The first issue is that in an ‘if’ expression, the condition has to match one of the clauses, or an exception is thrown. This means you can’t write simple code like


if Logging -> log("something") end

and instead you have to write


if Logging -> log("something"); true -> ok end

This requirement may seem annoying, but it is there for a good reason. In Erlang, all expressions (except for ‘exit()’) must return a value. You should always be able to write

A = foo();

and expect A to be bound to a value. There is no “null” value in Erlang (the ‘undefined’ atom usually takes its place).

Fortunately, Erlang lets you get around this issue with a one-line macro:


-define(my_if(Predicate, Expression), if Predicate -> Expression; true -> undefined end).

Then you can use it as follows:


?my_if(Logging, log("something"))

It’s not that bad, is it?

This solution does have a shortcoming, though, which is that it only works for a single-clause ‘if’ expression. If it has multiple clauses, you’re back where you started. That’s where you should take a second look at LFE :)

The second issue about ‘if’ expressions is that you can’t call any user- defined function in the conditional, just a subset of the Erlang BIFs. You can get around this limitation by using ‘case’, but again you have to provide a ‘catch all’ clause. For a single clause, you can simply change the macro I created to use a case statement.


-define(case(Predicate, Expression), case Predicate -> Expression; _ -> undefined end).

For an arbitrary number of clauses, a macro won’t help, and this is something you’ll just have to live with. If it really bothers you, use LFE.

3. Strings

The perennial complaint against Erlang is that it “sucks” for strings. Erlang represents strings as lists of integers, and for some reason many people are convinced that this is tantamount to suckage.

…you can’t distinguish easily at runtime between a string and a list, and especially between a string and a list of integers.

A string *is* a list of integers — why should we not represent it as such? If you care about the type of list you’re dealing with, you should embed it in a tuple with a type description, e.g.


{string, "dog"},
{instruments, [guitar, bass, drums]}

But if you don’t care what the type is, representing a string as a list makes a lot of sense because it lets you leverage all the tools Erlang has for working with lists.

A real issue with using lists is that it’s a memory-hungry data structure, especially on 64 bit machines, where you need 128 bits = 16 bytes to store each character. If your application processes such massive amounts of string data that this becomes a bottleneck, you can always use binaries. In fact, you should always use binaries for “static” strings on which you don’t need to do character-level manipulation in code. ErlTL, for example, compiles all static template data as binaries to save memory.

Erlang string operations are just not as simple or easy as most languages with integrated string types. I personally wouldn’t pick Erlang for most front-end web application work. I’d probably choose PHP or Python, or some other scripting language with integrated string handling.

I disagree with this statement, but instead of rebutting it directly, I’ll suggest a new kind of Erlang challenge: build a webapp in Erlang and show me how string handling was a problem for you. I’ve heard a number of people say that Erlang’s string handling is a hinderance in building webapps, but by my experience this simply isn’t true. If you ran into real problems with strings when building a webapp, I would be very interested in hearing about them, but otherwise it’s a waste of time hypothesizing about problems that don’t exist.

4. Functional Programming Mismatch

The issue here is that Erlang’s variable immutability supposedly makes writing test code difficult.

Immutable variables in Erlang are hard to deal with when you have code that tends to change a lot, like user application code, where you are often performing a bunch of arbitrary steps that need to be changed as needs evolve.

In C, lets say you have some code:

int f(int x) {
x = foo(x);
x = bar(x);
return baz(x);
}

And you want to add a new step in the function:

int f(int x) {
x = foo(x);
x = fab(x);
x = bar(x);
return baz(x);
}

Only one line needs editing,

Consider the Erlang equivalent:

f(X) ->
X1 = foo(X),
X2 = bar(X1),
baz(X2).

Now you want to add a new step, which requires editing every variable thereafter:

f(X) ->
X1 = foo(X),
X2 = fab(X1),
X3 = bar(X2),
baz(X3).

This is an issue that I ran into in a couple of places, and I agree that it can be annoying. However, discussing this consequence of immutability without mentioning its benefits is missing a big part of the picture. I really think that immutability is one of Erlang’s best traits. Immutability makes code much more readable and easy to debug. For a trivial example, consider this Javascript code:


function test() {
  var a = {foo: 1; bar: 2};
  baz(a);
  return a.foo;
}

What does the function return? We have no idea. To answer this question, we have to read the code for baz() and recursively descend into all the functions that baz() calls with ‘a’ as a parameter. Even running the code doesn’t help because it’s possible that baz() only modifies ‘a’ based on some unpredictable event such as some user input.

Consider the Erlang version:


test() ->
  A = [{foo, 1}, {bar, 2}],
  baz(A),
  proplists:get_value(A, foo).

Because of variable immutability, we know that this function returns ‘1′.

I think that the guarantee that a variable’s value will never change after it’s bound is a great language feature and it far outweighs the disadvantage of having to use with unique variable names in functions that do a series of modifications to some data.

If you’re writing code like in Damien’s example and you want to be able to insert lines without changing a bunch of variable names, I have a tip: increment by 10. This will prevent the big cascading variable renamings in most situations. Instead of the original code, write


f(X) ->
  X10 = foo(X),
  X20 = bar(X10),
  baz(X20).

then change it as follows when inserting a new line in the middle:


f(X) ->
  X10 = foo(X),
  X15 = fab(X10),
  X20 = bar(X15),
  baz(X20).

Yes, I know, it’s not exactly beautiful, but in the rare cases where you need it, it’s a useful trick.

This issue could be rephrased as a complaint against imperative languages: “I don’t know if the function to which I pass my variable will change it! It’s too hard to track down all the places in the code where my data could get mangled!” This may sound outlandish especially if you haven’t coded in Erlang or Haskell, but that’s how I really feel sometimes when I go back from Erlang to an imperative language.

Erlang wasn’t a good match for tests and for the same reasons I don’t think it’s a good match for front-end web applications.

I don’t understand this argument. Webapps need to be tested just like any other application. I don’t see where the distinction lies.

5. Records

Many people hate records and on this topic I fully agree with Damien. I think the OTP team should just integrate Recless into the language and thereby solve most of the issues people have with records.

If you really hate records, another option is to use LFE, which automatically generates getters and setters for record properties.

Incidentally, if you use ErlyWeb with ErlyDB, you probably won’t use records at all and you won’t run into these annoyances. ErlyDB generates functions for accessing object properties which is much nicer than using the record syntax. ErlyDB also lets you access properties dynamically, which records don’t allow, e.g.


P = person:new_with([{name, "Paul"}]),
Fields = person:db_field_names(),
[io:format("~p: ~p~n", [Field, person:Field(P)]) || Field <- Fields]

Records are ugly, but if you’re creating an ErlyWeb app, you probably won’t need them. If they do cause you a great deal of pain, you can go ahead and help me finish Recless and then bug the OTP team to integrate it into the language :)

6. Code oragnization

Every time time you need to create something resembling a class (like an OTP generic process), you have to create whole Erlang file module, which means a whole new source file with a copyright banner plus the Erlang cruft at the top of each source file, and then it must be added to build system and source control. The extra file creation artificially spreads out the code over the file system, making things harder to follow.

I think this issue occurs in many programming languages, and I don’t think Erlang is the biggest offender here. Unlike Java, for instance, Erlang doesn’t restrict you to defining a single data type per module. And Ruby (especially Rails) applications are also known for having multitudes of small files. In Erlang, you indeed have to create a module per gen-server and the other ‘behaviors’ but depending on the application this may not be an issue. However, I don’t think there’s anything wrong with keeping different gen-servers in different modules. It should make the code more organized, not less.

7. Uneven Libraries and Documentation

I haven’t had a problem with most libraries, and in cases where they do have big shortcomings you can often find a good 3rd party tool. The documentation is a pain to browse and search, but gotapi.com makes some of this pain go away.

Summary

Is Erlang perfect? Certainly not. But sometimes people exaggerate Erlang’s problems or they don’t address the full picture.

Here are some suggestions I have for improving Erlang:

- Add a Recless-like functionality to make working with records less painful.
- Improve the online documentation by making it easier to browse and search.
- Make some of the string APIs (especially the regexp library) also work with binaries and iolists.
- Add support for overloading macros, just like functions.
- Add support for Smerl-style function inheritance between modules.

Like any language, Erlang has some warts. But if it were perfect, it would be boring, wouldn’t it? :)

Your Daily Dose of Erlang Evangelism

Posted by Yariv on February 27, 2008

This is a good one I fished from the mailing list:

Using Erlang continually makes me both smile and cry at the same time. I smile because of the overall simplicity it brings to solving all those hard issues I mentioned above, but I also cry knowing how many hours, days, weeks, and months my former colleagues and I spent trying
to solve all those really hard issues.

http://www.nabble.com/Steve-Vinosky-interview-to15709698.html#a15720750

Seaside-Style Programming in ErlyWeb

Posted by Yariv on February 17, 2008

The Arc Challenge started an interesting thread in the ErlyWeb mailing list about continuations-driven web frameworks. ErlyWeb doesn’t have built-in support for continuations, but Arc does and so does Seaside. I haven’t paid much attention to the use of continuations in web frameworks before the Arc challenge, but I became especially interested in experimenting with them after seeing Seaside solution.

In case you haven’t read it, this is the requirement of the Arc challenge:

Write a program that causes the url said (e.g. http://localhost:port/said) to produce a page with an input field and a submit button. When the submit button is pressed, that should produce a second page with a single link saying “click here.” When that is clicked it should lead to a third page that says “you said: …” where … is whatever the user typed in the original input field. The third page must only show what the user actually typed. I.e. the value entered in the input field must not be passed in the url, or it would be possible to change the behavior of the final page by editing the url.

This is the original Arc solution:


(defop said req
  (aform [w/link (pr "you said: " (arg _ "foo"))
              (pr "click here")]
     (input "foo")
     (submit)))

This is how you would write the same logic in Erlang, if it had an Arc-like web framework:


said(A) ->
  form(
     fun(A1) ->
       link(fun(A2) -> ["you said ", get_var(A1, "foo")] end,
         "click here")
     end,
     [input("foo"),
      submit()]).

The Erlang code is a bit more verbose, mostly because Erlang macros don’t allow you to hide the “fun() -> … end” syntax the way Lisp macros let you hide the (lambda ..) keyword.

This is the Seaside solution:


| something |
  something := self request: 'Say something'.
  self inform: 'Click here'.
  self inform: something

IMO, this solution cheats a bit by using high-level functions for generating the HTML tags whereas the Arc version generates them explicitly. However, putting minor complaints aside, I think the Seaside version is the winner in readability. As a reddit comment said, it reads like prose. It doesn’t even declare any closures explicitly — it says exactly what it does and nothing more.

Wouldn’t it be cool if we could use this style of programming in ErlyWeb applications?

Fortunately, we can! I hacked a continuations plugin for ErlyWeb that lets you write Seaside-style code so fans of this programming style would feel at home with ErlyWeb. (This is all done in 105 lines of code :) ) Before I explain how the plugin works, I’ll show you how to create an ErlyWeb controller that implements the the Arc challenge using this plugin:


-module(said_controller).
-compile(export_all).
-import(continuations, [ask/2, confirm/2, pr/1]).
 
index(A) ->
    continuations:do(
      A, fun(K) ->      
                 Name = ask(K, "name"),
                 confirm(K, "click here"),
                 pr(["your name: ", Name])
         end).

This may seem more verbose than the Seaside code because of the module declarations at the top, but the “meat” is about the same. (I could make this code even smaller by integrating continations.erl deep into ErlyWeb, which would remove the explicit call to continuations:do(). However, I didn’t want to go too far with this proof of concept.)

How does this work? Using concurrency, of course! For each “continuation”, the plugin spawns a process and registers it in a Mnesia table according to a randomly generated key. The key (K) is encoded in the URLs to which the <form> and <a> tags point. When a request arrives, continuations:do() looks up the process in Mnesia and sends it a message of the type {A, self()}. The process does some work and sends back in reply the HTML to be rendered, and waits for the next message. The web server process receives the rendered HTML and sends it to the client using the normal ErlyWeb mechanisms.

If a process doesn’t receive a message in 10 seconds, it dies and removes itself from the Mnesia table, which provides automatic garbage collection to stale sessions.

You can get the code for continuations.erl here. Just remember it’s a proof of concept and I don’t recommend using it in a production environment.

(Before you use it in your application, make sure you call continuations:start().)

Final word: IMO, although continuations help write more natural code in certain multi-page interactions, most of the logic in web applications involves rendering dynamic pages for RESTful URLs. So, if your web framework doesn’t support continuations, don’t worry about it too much. It’s likely the code for your application wouldn’t be dramatically smaller if you could write it with the use of continuations. (That said, take my advice with a grain of salt. I haven’t used a continuations based framework to build a real application, so I may be missing something.)

More Erlang Fun: Distributed, Fault Tolerant MapReduce

Posted by Yariv on February 10, 2008

I the Erlang Challenge posting, I showed how to implement a simple distributed parallel map function. You can pass it a list of nodes, and it performs each application of F1 on a random node (”node” means a connected Erlang VM that may exist on a different machine) from the list. Finally, it collects the results in the order of the arguments.

What Erlang nailed beside concurrent and distributed programming is fault tolerance. When processes die or nodes go down, the VM notifies their supervisors so they can perform error recovery. My previous example didn’t demonstrate this capability, so in this article I’ll show how to implement a similar solution but also taking into account node failures. In addition, instead of doing just a simple parallel map, this example will also fold the results, making it a basic MapReduce implementation.

(The reason I’m adding the ‘reduce’ phase is because it allows me to not worry about the order at which the results are accumulated. This implies that the reduce operation must be commutative.)

The function mapreduce() takes a function F1 and applies to elements of list L. Each application is performed on a remote node from Nodes. As opposed to the pmap() example, this implementation chooses the next node from the list in a rotating fashion instead of randomly (i.e., if Nodes is [A, B, C], then F1 is applied on remote nodes with the following order: A, B, C, A, B, C, A …). The results are collected, and if any nodes went down, their computations are retried on the remaining nodes until all computations succeed or no nodes are left. If the computations all succeed, we call lists:foldl(F2, Acc, Vals), where Vals contains the results of the computations.

Implementation details:

- do_map() returns a list of {Pid, X} tuples. Pid is the process on the remote node in which F1 was applied to X. The reason we want to remember X is that if Pid’s node goes down, we can later retry to evaluate F1(X) on a different node.
- For each {Pid, X} tuple, collect() receives either an {ok, Pid, Val} message, indicating the computation was performed successfully, or {nodedown, Node}, indicating the node on which Pid lived went down. collect() folds the result into a tuple of type {Successes, Failures, RemainingNodes}. If Failures is not empty, collect() calls do_map() on the list of Failures and the remaining nodes, and then recursively calls itself on the result and appends the outcome to Successes.

The code is below. (Note that I didn’t test it, so it may have bugs :) )


-module(mapreduce).
-export([mapreduce/5]).
 
mapreduce(F1, F2, Acc, L, Nodes) ->
  %% map F1 to the elements of L on nodes from Nodes
  Results = do_map(F1, L, Nodes),
 
  %% collect the results, retrying in the event of failures
  Vals = collect(F1, Results, Nodes),
 
  %% perform the reduce operation
  lists:foldl(F2, Acc, Vals).

 
%% exit if we ran out of good nodes
do_map(_F1, _L, []) -> exit(no_nodes);
do_map(F1, L, Nodes) ->
  Parent = self(),
 
  %% apply F1 to values of L on remote nodes in a rotating fashion
  {Results, _Nodes1} =
    lists:foldl(
      fun(X, {Acc, [Node | Rest]}) ->
        erlang:monitor_node(Node),
        Fun = fun() -> Parent ! {ok, self(), (catch F1(X))} end,
        Pid = spawn(Node, Fun),
        {[{Pid, X} | Acc], Rest ++ [Node]}
      end, {[], Nodes}, L),
  Results.
 
collect(F1, Results, Nodes) ->
  collect(F1, Results, Nodes, []).
collect(F1, Results, Nodes, Acc) ->
  {Successes, Failures, RemainingNodes}=
    lists:foldl(
      fun({Pid, X}, {Successes1, Failures1, Nodes1}) ->
        Node = node(Pid),
        receive
          {ok, Pid, Val} ->
            {[Val | Successes1], Failures1, Nodes1};
 
          %% we may receive this message because of call to
          %% monitor_node()
          {nodedown, Node} ->
            {Successes1, [X | Failures1], Nodes1 -- Node}
        end
      end, {[], [], Nodes}, Results),
  
  if Failures =/= [] ->
      %% retry the failed computations on the remaining nodes
      %% and add the results to the current list of successes
      Results2 = do_map(F1, Failures, RemainingNodes),
      collect(F1, Results2, RemainingNodes, Successes ++ Acc);
    true ->
      Successes ++ Acc
  end.

This is more complex than the pmap() example, but I think it packs a lot of functionality into a relatively small amount of code

Erlang Challenge Followup

Posted by Yariv on February 08, 2008

After I published the Erlang Challenge, I saw a few comments on Hacker News and Reddit repudiating it with the following arguments, to which I’d like to respond:

- It was a snide response to the Arc challenge.

Maybe I should have read the article a couple of times over before I published it late last night, because if it came off as snide, I didn’t communicate it as I had intended. I’ve found the Arc challenge fun and interesting (I even submitted my own Erlang solution), and I wouldn’t intentionally respond to it in a snide way. The reason I said the Erlang challenge is in the spirit of the Arc challenge is that the Arc challenge highlights Arc’s strengths, whereas the Erlang challenge highlights Erlang’s strengths.

- It was rigged in favor of Erlang.

That was basically the point. I wanted the Erlang challenge to demonstrate the kinds of problems that Erlang excels at solving. Maybe I should have made this clearer when I wrote the article — it was more of an illustration of Erlang’s strengths than a “real” challenge. I wasn’t really expecting other languages to compete with Erlang on its home turf, the same way I wouldn’t expect Erlang to compete with, say, C or C++ at low-level systems programming or with ActionScript at Flash programming.

- It showed that Erlang is good at solving a narrow scope of problems, but I didn’t show that Erlang is a good general purpose tool.

I don’t think it’s possible to show in 10 lines of code how good (or bad) a language is at solving anything but problems that are very similar to those in the example. The only true way to judge whether a language is good at building certain types of systems is to build them using the language and evaluate the results.

I know that people have used Erlang successfully to build phone switches, a MMORPG, a massively scalable DBMS, web applications, a collaboration application, high performance messaging servers, ad servers, web servers, a scalable poker server, and a 3D modeling tool. When I say that Erlang is a good language for building certain applications, that’s what I base it on.

Edit: I forgot to list CouchDB, an open source document storage system, and Triggit, a widget server unlike any other.

The Erlang Challenge

Posted by Yariv on February 08, 2008

In the spirit of Paul Graham’s Arc challenge, and in spite of the “controversy” around it, I devised a programming challenge of my own. It’s called the Erlang Challenge.

The goal is to implement an advanced “parallel map”: a function (F1) that applies another function (F2) to every element (E) of a list (L1) concurrently and then gathers the results in a new list (L2). The order of results in L2 corresponds to the order of elements in L1, and the elements of L1 are guaranteed to remain unchanged no matter what function F2 does. To make things more interesting, the F1 should also accept a list of cluster nodes (NL) onto which the computations can be distributed. Each application of F2 to an element of L1 should be done on a random node from NL, and the result should be send back to the node on which F1 was called (N). If NL is empty, all computations should occur locally on N.

This challenge isn’t just about succinctness, but, like most real-world systems, also about scalability and performance. Therefore, it has the following requirements:

- The implementation must be able to spawn at least 1 million concurrent processes on a modern server machine.
- On multi-core machines, the application’s performance must increase (almost) linearly with the number of available CPUs.
- The application as a whole must exhibit soft real-time performance. Specifically, this means that processes won’t freeze for a long time during global garbage collection sweeps, and that no process will be able to block the entire VM to do IO or call native code, regardless of what F2 does. In other words, it is impossible to create a function F2 that could block the scheduler while F2 executes.

Here’s how I would implement it in Erlang (this is loosely based on Joe Armstrong’s pmap implementation):


pmap(Fun, List, Nodes) ->
  SpawnFun =
    case length(Nodes) of
       0 -> fun spawn/1;
       Length ->
         NextNode = fun() -> nth(random:uniform(Length), Nodes) end,
         fun(F) -> spawn(NextNode(), F) end
    end,
  Parent = self(),
  Pids = [SpawnFun(fun() -> Parent ! {self(), (catch Fun(Elem))} end)
    || Elem <- List],
  [receive {Pid, Val} -> Val end || Pid <- Pids].

How would you implement it in your favorite language?

Update: please read the followup.

Debugging with Distel

Posted by Yariv on January 29, 2008

Until recently, I’ve done my Erlang debugging either using the built-in debugger or not using a debugger at all and instead relying on the good old io:format() function. The reason I haven’t been keen on using the Erlang debugger is that it’s not integrated into my editor (I use Emacs). To debug a file, I’d have to open it separately in the debugger UI, where I could set breakpoints and step through the code. For simple bugs, this is more effort than adding a few io:format() statements, reloading the page, and figuring out the bug from the output.

I’m not an IDE fanatic (well, duh, I use Emacs) — I can do fine without most of the features in advanced IDEs — but I do expect any decent editor to have at least 3 features: syntax highlighting, auto-indentation, and integrated debugging.

(I’m ambivalent about IDEs with code completion. It adds convenience, but it also it lets you get by without really remembering your APIs. Without code completion, I remember most of the my application’s function names and their parameters simply because not remembering them slows me down too much. This is akin to the cellphone effect on remembering numbers: before I had a cellphone, I remembered all my close friends and family members’ numbers. Looking them up in an address book was too much work. Now, I remember just a fraction of them. My cellphone made me lazy.)

The Emacs mode for Erlang provides syntax highlighting and auto-indentation, but no integrated debugging. AFAIK, neither Erlide nor ErlyBird have integrated debuggers. I knew Distel was supposed to add debugging support, and I even tried setting it up a while ago but gave up after running into problems I couldn’t figure out. Last weekend, though, I finally got it working after following Bill Clemenson’s Distel tutorial. I must say that having a debugger integrated into Emacs is a huge improvement. I recommend it to every Erlang hacker.

My only peeve with this debugger is that you must mark each file you want to debug as interpreted (the Emacs shortcut is “C-c C-d i”) before you can set breakpoints in it and step into its functions. This seems unnecessary — Erlang .beam files usually contain metadata including the location of their source files. I wish the debugger used this information to auto-interpret all the files you’re trying to debug.

This is a relatively small peeve, though. The Distel debugger is definitely a step up from io:format().

I’m still hoping someone will create a more user-friendly Erlang IDE with a slick UI and an integrated debugger. I like the TextMate interface, but TextMate doesn’t do auto-indentation for Erlang or debugging. I’ll stick to Emacs+Distel for now.

How to Use Concurrency to Improve Response Time in ErlyWeb Facebook Apps

Posted by Yariv on January 22, 2008

When I was building the Vimagi Facebook app, I came across a common scenario where using concurrency can make your application more responsive for its users.

The typical flow of responding to requests coming from Facebook looks like this:

1) request arrives
2) do some stuff (mostly DB CRUD operations)
3) call Facebook API to send notifications / update newsfeeds and profile FBML
4) send response

When you’re building a facebook app with ErlyWeb, you can instead do the following:

1) request arrives
2) do some stuff (mostly DB CRUD operations)

spawn(fun() ->
  3) call Facebook API to send notifications / update newsfeeds and profile FBML
end)

4) send response

The Facebook API calls in step 3 are much more expensive than the typical ErlyWeb controller operations because these calls involve synchronous round trips to the Facebook servers plus XML processing for the responses. By performing the Facebook API calls in a new process we can return the rendered response to the browser immediately and let the Erlang VM schedule the Facebook API calls to happen leisurely in the background.

The only gotcha is that if an error occurs in the spawned process we can’t notify the user right away — but this isn’t really problem because we can log the errors and retry later, which is arguably a better approach anyway from a usability standpoint.

It’s really that easy! A simple call to spawn() makes our app *feel* much faster. This puts the debate around language performance comparisons in a new light: how do you take into account the observation that some languages make “cheating” so much easier? :)

Vimagi: The Facebook App

Posted by Yariv on January 20, 2008

I finally finished the Vimagi Facebook app! Check out the screen shots:

vimagi-fb1.png

vimagi-fb2.png

The app works similar to the vimagi.com website: you can create paintings with titles, tags, and descriptions and share them with anyone. Other Facebook users can comment on the paintings and rate them. Paintings created in Facebook are automatically posted to vimagi.com, where vimagi.com users can also add comments and ratings. All paintings have embed codes you can use to embed them on any site (with playback). In Facebook, you can create paintings for your Facebook friends and those paintings will appear on their (and your) profile. Similar to vimagi.com, the Facebook app has a gallery and a tags page, which contains content from both vimagi.com and the Facebook app.

I created the Facebook app mostly as a learning exercise, but I also believe that the Vimagi features would appeal to more people by being embedded into people’s their existing social network rather than in a standalone site.

Unfortunately, I underestimated how tricky it would be to port the vimagi.com features and code into Facebook without breaking the site and while seamlessly blending the paintings and data created on Facebook and on vimagi.com. (One of the pesky issues I encountered is that Facebook applications have root URLs of the form http://apps.facebook.com/[app name], which was incompatible with the vimagi.com relative URLs. The existing URLs all start with a forward slash, assuming they follow immediately after the domain name. My life would have been much better if Facebook used the url scheme “http://[app name].apps.facebook.com/” for Facebook apps as it would have allowed existing URLs to remain unmodified.)

If you like to paint, I hope you enjoy the applications, and if you have friends who like to paint, please send them an invitation. Facebook needs some more Erlang-generated pageviews :)

If you like Vimagi and you’re on Facebook, please join the Vimagi fan club. Also, I’ll appreciate it if you let me know of any feedback or suggestions you may have.

P.S. Thanks I to Bryan Fink for the excellent erlang2facebook library.