I heard about Google Gears when it came out a few weeks ago, but only a couple of days I took it for a short test drive. I was surprised to see that Google Gears uses Erlang-style concurrency (well, sort of).
Google Gears supports asynchronous background processing using the WorkerPool module, which lets your Google Gears app execute multiple tasks concurrently without blocking the main UI thread. What I find interesting thing about WorkerPool is that threads only communicate by message passing and that there is no API for synchronizing access to objects that are passed around between threads. This is a departure from the traditional concurrency toolbox in imperative languages -- using synchronized access to shared memory.
It's so easy to use it's almost... Erlangy :)
So how does Google Gears pulls off this feat of simplicity?
Google Gears only lets you send strings between threads. Since Javascript strings are immutable, there's no danger in letting multiple threads share them.
This solution is clever, but it comes at a cost -- you must serialize your objects before sending them between threads, which incurs a performance penalty (some extra coding, too). I don't expect the performance penalty to noticeably impact most Google Gears apps, though, which are mostly offline adaptations of standard webapps.
Google Gears has managed to pull off easy concurrency in an imperative language by relying on the immutability of strings in Javascript, but this approach is only practical on the desktop. In a server app with higher performance and scalability requirements, sending serialized objects would probably slow things down too much.
All in all, I think Google Gears is a great solution for adding offline support to webapps, and it gets bonus points for using Erlang-style concurrency. Now I'm just waiting for Safari support :)
Sunday, July 22, 2007
Wednesday, July 11, 2007
San Francisco ErlLounge, Jul 18th
About a month ago, I packed my stuff, said farewell to Boston, and moved to the San Francisco Bay Area. I currently live in the town of San Mateo, where occasionally cars drive by and people walk their dogs. It's very exciting. I will soon move to Palo Alto for a few months, and then who knows. Whatever it is, it won't be as lively as San Mateo.
I'm planning on attending the San Francisco ErlLounge on Jul 18th. Visit http://umichigan.facebook.com/p.php?i=2223888&k=33553ba7bd for details. You should attend if you can. I'll tell you thrilling stories about San Mateo. I promise.
On an unrelated note, the spammers got me beat, and I finally gave up and disabled comments on this blog. If you want to share your thoughts, you can email me at yarivsblog at gmail dot com.
I'm planning on attending the San Francisco ErlLounge on Jul 18th. Visit http://umichigan.facebook.com/p.php?i=2223888&k=33553ba7bd for details. You should attend if you can. I'll tell you thrilling stories about San Mateo. I promise.
On an unrelated note, the spammers got me beat, and I finally gave up and disabled comments on this blog. If you want to share your thoughts, you can email me at yarivsblog at gmail dot com.
Friday, June 08, 2007
Concurrency Buzz
There's been quite a buzz lately in the programming blogsphere about concurrent programming. I think the software world is paying increasing attention to concurrent programming (and Erlang) due to the following reasons:
- The apparent decline of Moor's law coupled with the rise of multi-core CPUs.
- The "traditional" (i.e. shared-memory based concurrency) employed by most languages is flawed: it's too hard to grasp, it's too error-prone, and it's not multi-core friendly.
- Joe Armstrong's book, Programming Erlang has made Erlang accessible to a wide audience.
- Reddit.
There's more to Erlang style concurrency than lightweight processes and message passing. I don't expect other (popular) languages to fully gain Erlang-style concurrency (not soon, at least) because to do so they would have to adopt the following combination of traits:
- A scheduler that uses a mixture of lightweight processes and system threads (SMP Erlang spawns a system thread per CPU).
- Immutable variables.
- General avoidance of long blocking calls to native code in libraries.
- A garbage collector that utilizes per-process heaps to avoid freezing the entire VM during sweeps.
- A revised approach to error handling based on supervision trees and non-defensive coding.
- Web servers, database drivers, libraries, tools, etc. that actually use the new concurrency primitives.
As the saying goes, never say "never"... :)
Does that mean that you need Erlang to make your applications utilize available CPUs on multi-core machines? Of course not. However, when 80 core machines start appearing in our data centers in a few years, at least some people will take comfort in Joe Armstrong's statement:
"Your Erlang program should just run N times faster on an N core processor"
- The apparent decline of Moor's law coupled with the rise of multi-core CPUs.
- The "traditional" (i.e. shared-memory based concurrency) employed by most languages is flawed: it's too hard to grasp, it's too error-prone, and it's not multi-core friendly.
- Joe Armstrong's book, Programming Erlang has made Erlang accessible to a wide audience.
- Reddit.
There's more to Erlang style concurrency than lightweight processes and message passing. I don't expect other (popular) languages to fully gain Erlang-style concurrency (not soon, at least) because to do so they would have to adopt the following combination of traits:
- A scheduler that uses a mixture of lightweight processes and system threads (SMP Erlang spawns a system thread per CPU).
- Immutable variables.
- General avoidance of long blocking calls to native code in libraries.
- A garbage collector that utilizes per-process heaps to avoid freezing the entire VM during sweeps.
- A revised approach to error handling based on supervision trees and non-defensive coding.
- Web servers, database drivers, libraries, tools, etc. that actually use the new concurrency primitives.
As the saying goes, never say "never"... :)
Does that mean that you need Erlang to make your applications utilize available CPUs on multi-core machines? Of course not. However, when 80 core machines start appearing in our data centers in a few years, at least some people will take comfort in Joe Armstrong's statement:
"Your Erlang program should just run N times faster on an N core processor"
Thursday, April 19, 2007
SlideAware: From Python to Ruby to Erlang
SlideAware has created a slick web-based solution for managing the life cycle of Powerpoint presentations. They started using Python, then they switched to Rails, and finally settled on pure Erlang. Using Erlang, they replaced a combination of Lighttpd + RoR + SQLlite + XMLRpc + Jython + Lucene with the much simpler and highly scalable stack of Yaws + Mnesia + Erlang.
They wrote an interesting article describing their technology choices. Check it out at http://slideaware.typepad.com/slideaware/2007/04/from_python_to_.html.
They wrote an interesting article describing their technology choices. Check it out at http://slideaware.typepad.com/slideaware/2007/04/from_python_to_.html.
Tuesday, April 17, 2007
Announcing ErlyWeb 0.6
I'm pleased to announce the release of ErlyWeb 0.6. This very exciting release contains a new Mnesia driver for ErlyDB created by Matthew Pflueger and a Postgres driver (based on the open source driver from Erlang Consulting) created by Roberto Saccon. Finally, you can use a single database abstraction layer in Erlang to access MySQL, Postrgres and Mnesia without writing a single line of SQL or DBMS-specific logic :)
You can get ErlyWeb 0.6 from the ErlyWeb website or from its Google Code repository. Make sure to check out CHANGELOG.txt for a complete list of changes.
If you have any questions regarding the new drivers, please ask the ErlyWeb Google Group.
You can get ErlyWeb 0.6 from the ErlyWeb website or from its Google Code repository. Make sure to check out CHANGELOG.txt for a complete list of changes.
If you have any questions regarding the new drivers, please ask the ErlyWeb Google Group.
An Erlang Pipe
Yahoo Pipes is one of the most innovative apps I've seen: it lets you program data feeds. More specifically, you can use Pipes build programs (called "pipes") that generate output feeds from input feeds by combining, filtering, and manipulating the inputs using different modules from the Pipes IDE. The IDE has a friendly graphical interface, letting you drag, drop and connect different components easily. The IDE is implemented in Javascript, which is impressive.
I couldn't resist playing with Pipes, so I created an Erlang pipe. The Erlang pipe reads the feeds from programming.reddit.com and lambda-the-ultimate.org, combines them, and filters the results by accepting only articles whose contents contain the word 'Erlang'. It then combines the result with the inputs from planeterlang.org.
Check out this pipe at http://pipes.yahoo.com/pipes/pipe.info?_id=4jVpGgjt2xGttygedrq02Q.
I couldn't resist playing with Pipes, so I created an Erlang pipe. The Erlang pipe reads the feeds from programming.reddit.com and lambda-the-ultimate.org, combines them, and filters the results by accepting only articles whose contents contain the word 'Erlang'. It then combines the result with the inputs from planeterlang.org.
Check out this pipe at http://pipes.yahoo.com/pipes/pipe.info?_id=4jVpGgjt2xGttygedrq02Q.
Sunday, April 15, 2007
ErlyWeb Cheat Sheet
Brian Olsen has created a very helpful cheat sheet for ErlyWeb. Check it out at http://www.pushon.org/erlyweb/cheat_sheet.html.
Thursday, March 15, 2007
Boston BarCamp2
Is anybody who reads this blog going?
http://barcamp.org/BarCampBoston2
I think I may go and try to talk some people into using Erlang in their startups.
Update (2 hours later):
It looks like my job is going to be easier than expected. I checked out the at the BarCamp real time visual aggregator (http://barcamp.org/BarCampNewsVisualAggregator), and guess what I saw?
Maybe yaws could be a better platform for serving the BarCamp aggregator: http://www.sics.se/~joe/apachevsyaws.html :)
http://barcamp.org/BarCampBoston2
I think I may go and try to talk some people into using Erlang in their startups.
Update (2 hours later):
It looks like my job is going to be easier than expected. I checked out the at the BarCamp real time visual aggregator (http://barcamp.org/BarCampNewsVisualAggregator), and guess what I saw?
TECH. PROBLEMS
This experiment may cause to much traffic for pbwiki... to be improved...
Maybe yaws could be a better platform for serving the BarCamp aggregator: http://www.sics.se/~joe/apachevsyaws.html :)
Thursday, March 01, 2007
Announcing ErlyWeb 0.5
I'm pleased to announce that ErlyWeb 0.5, the biggest and best release yet, is now available to download from erlyweb.org. There's a long list of improvements and fixes, of which you can read here.
As I indicated in a previous posting, ErlyWeb no longer supports app views. If you are upgrading from an earlier version, you should convert your app view to an html container and use the 'phased' return value from the app controller, as described in the updated documentation on the website.
ErlyWeb is still in alpha, and will likely remain this way until somebody has used it to build a complete production app. So, who will reach the finish line first? :)
As I indicated in a previous posting, ErlyWeb no longer supports app views. If you are upgrading from an earlier version, you should convert your app view to an html container and use the 'phased' return value from the app controller, as described in the updated documentation on the website.
ErlyWeb is still in alpha, and will likely remain this way until somebody has used it to build a complete production app. So, who will reach the finish line first? :)
Friday, February 23, 2007
ErlyWeb Tutorial: Life In the Intersection of FP and Dynamic HTML
Web applications often have pages in which the user can edit the fields of an existing database record and then save the updated record by submitting the form. To generate such an 'edit' page, you generally have to read the record from the database, generate an HTML form whose fields are pre-populated with the record's data, and then send the form back to the browser. Let's look at a few ways of doing this with ErlyWeb (for instructional purposes, we won't be using "-erlyweb_magic(on)." :) ).
Let's suppose we want to provide a form for editing the records of a database table called 'language'. We can create an ErlyDB module for working with this table as follows:
language.erl:
When we call erlyweb:compile() (or when ErlyWeb auto compiles our modules becase we have turned 'auto_compile' on, as one should do during development), ErlyDB reads the metadata for the 'language' database table and generates a large number of functions in the 'language' module covering the whole exciting CRUD gamut.
Now let's create a basic controller implementing the data access logic for the 'language edit' page. I will only show the most stripped-down logic necessary for rendering the 'edit' form, which is the focus of this tutorial.
language_controller.erl:
When ErlyWeb receives a request such as "http://myapp.com/language/edit/1", it will invoke the 'edit/2' function in language_controller. This function will look up the database record using a SQL query such as "SELECT * FROM language WHERE id=1", and send the resulting record directly to the view function, or the tuple {error, no_such_record} if no record matches the requested id.
Now let's take a first pass at making a the view for this component (note that in this tutorial, we'll avoid any fancy automatic form-generation methods and write most of the HTML by hand).
language_view.et:
Let's suppose we want to provide a form for editing the records of a database table called 'language'. We can create an ErlyDB module for working with this table as follows:
language.erl:
-module(language).
When we call erlyweb:compile() (or when ErlyWeb auto compiles our modules becase we have turned 'auto_compile' on, as one should do during development), ErlyDB reads the metadata for the 'language' database table and generates a large number of functions in the 'language' module covering the whole exciting CRUD gamut.
Now let's create a basic controller implementing the data access logic for the 'language edit' page. I will only show the most stripped-down logic necessary for rendering the 'edit' form, which is the focus of this tutorial.
language_controller.erl:
-module(language_controller).
-export([edit/2]).
edit(A, Id) ->
case language:find_id(Id) of
undefined ->
{data, {error, no_such_record}};
Language ->
{data, Language}
end.
When ErlyWeb receives a request such as "http://myapp.com/language/edit/1", it will invoke the 'edit/2' function in language_controller. This function will look up the database record using a SQL query such as "SELECT * FROM language WHERE id=1", and send the resulting record directly to the view function, or the tuple {error, no_such_record} if no record matches the requested id.
Now let's take a first pass at making a the view for this component (note that in this tutorial, we'll avoid any fancy automatic form-generation methods and write most of the HTML by hand).
language_view.et:
<%@ edit({error, no_such_record}) %>
ouch, the record doesn't exist
<%@ edit(Language) %>
Monday, February 12, 2007
Introducing... The ErlyWeb Logo!
My plea for help in the graphics design department in the making of an ErlyWeb logo has yielded a few candidates, and although I sincerely appreciate the efforts of the people who made them, and they gave me some good ideas, none of them has truly captured my fancy. So, I decided to take matters into my own hands and finally create a real logo for ErlyWeb.
Without further ado, I hereby present ErlyWeb's shiny new logo:

(This is the big version. A small version is on http://erlyweb.org.)
This logo is inspired by Erlang, Big Sur and Lisa Simpson.
(When I wrote that my graphic design skills were non-existent, you didn't think it really meant there was a Picasso inside me trying to break out, did you? :-) )
Without further ado, I hereby present ErlyWeb's shiny new logo:
(This is the big version. A small version is on http://erlyweb.org.)
This logo is inspired by Erlang, Big Sur and Lisa Simpson.
(When I wrote that my graphic design skills were non-existent, you didn't think it really meant there was a Picasso inside me trying to break out, did you? :-) )
Wednesday, February 07, 2007
ErlyWeb Tutorial: Using Components To Create Dynamic Containers
ErlyWeb's app views are a basic mechanism for sharing common view elements between components. An app view is a simple Erlang module or ErlTL template file that nests pre-rendered components in static (generally HTML) content. Most simple applications probably have an app view that looks like this:
This mechanism works well when your components share only static view elements. But what do you do when your components share dynamic view elements? If this is the case, you have a few options:
- You could put the logic for generating the dynamic content in the view module. However, this would be ugly, and it would go against one of ErlyWeb's primary purposes, which is to help you create a clean separation between application logic and view logic. Let's forget this option and move on.
- If all your components share an identical set of dynamic elements, you can change your app controller's hook function, so instead of returning '{ewc, A}', it returns a list containing the 'ewc' and/or 'data' tuples common to all components. For example, if all your pages have sidebar, your hook/1 function could return '[{ewc, sidebar, [A]}, {ewc, A}]'. This tells ErlyWeb render each component in the list, and then pass into the app view a 2 element list, wherein the first element is the rendered sidebar and the second element is the rendered component that corresponds to the browser's request. To properly handle this new parameter type, you could change your app view as follows:
My App
<% Data %>
This mechanism works well when your components share only static view elements. But what do you do when your components share dynamic view elements? If this is the case, you have a few options:
- You could put the logic for generating the dynamic content in the view module. However, this would be ugly, and it would go against one of ErlyWeb's primary purposes, which is to help you create a clean separation between application logic and view logic. Let's forget this option and move on.
- If all your components share an identical set of dynamic elements, you can change your app controller's hook function, so instead of returning '{ewc, A}', it returns a list containing the 'ewc' and/or 'data' tuples common to all components. For example, if all your pages have sidebar, your hook/1 function could return '[{ewc, sidebar, [A]}, {ewc, A}]'. This tells ErlyWeb render each component in the list, and then pass into the app view a 2 element list, wherein the first element is the rendered sidebar and the second element is the rendered component that corresponds to the browser's request. To properly handle this new parameter type, you could change your app view as follows:
<%? [Sidebar, Content] = Data %>
My App
<% Sidebar %>
<% Content %>
Again, this works well when all your pages have the same sidebar component. However, what if different pages have different sidebar components? Or what if some pages have a sidebar and some don't? Let's look at the next option.
- Starting from ErlyWeb 0.4, your controllers can return a {response, Elems} tuple, whose Elems list may include the {app_view_param, Ewc} tuple (for more info on the 'response' tuple, check out the ErlyWeb docs. Similar to the first option, this response tells ErlyWeb to render the (list of) 'ewc' and/or 'data' tuples contained in the Ewc variable, and then pass into the app view a 2-element list containing the rendered components followed by the rendered content for the request. In addition, you can use the {app_view, ModuleName} tuple to pick a specific app view (or disable the app view entirely) instead of using the default one.
This approach works, and it offers greater conveniece in customizing the dynamic app view data for each component (or each component function), but I don't like it. The reason is that I don't think that components should know or control what happens in the areas of the page outside of their own rendered HTML. In addition, it gets confusing if multiple nested sub-components define the 'app_view_param' element in their responses: which one should ErlyWeb be use? Given these shortcomings, I'm considering deprecating if not outright removing support for the 'app_view' and 'app_view_param' tuples from the 'response' tuple in ErlyWeb (it is and an experimental feature, after all, as the documentation notes). So, let's look at the next option.
- The last, and arguably best approach, is to use ErlyWeb's component system to create dynamic containers. A container is a component that contains one or more sub-components, which are given to the container as parameters. The container tells ErlyWeb to render its parameters together with any dynamic data specific to the container. For example, to create a container that renders its sub-component(s) together with a sidebar, you could use the following code:
sidebar_container_controller.erl:
-module(sidebar_container_controller).
-compile(export_all).
%% This tells ErlyWeb to reject browser requests that try to
%% access this component directly
private() -> true.
index(A, Ewc) ->
[{ewc, sidebar, A}, Ewc].
sidebar_container_view.et:
<% Data %>
<%@ index([Sidebar, Contents]) %>
<% Sidebar %>
<% Contents %>
Using this container, any component can render a subcomponent with a sidebar by simply returning the tuple '{ewc, sidebar_container, [A, {ewc, subcomponent, [A]}]}'. What I like about this container, beside its flexibility and reusability, is that it doesn't require any special logic nor does it rely on any features beyond ErlyWeb's simple and elegant component model.
An ideal place to use this container is in the app controller's hook function. For example, to achieve the same effect as the app view-based approach from the second option, you could implement this function as follows:
hook(A) ->
Ewc = erlyweb:get_initial_ewc({ewc, A}, myapp_erlyweb_data),
{ewc, sidebar_container, [A, Ewc]}.
(The first line may look a bit cryptic -- indeed, it relies on a function that isn't included yet in the official distribution -- but just know that it enforces the 'private' policies on component requests that come from clients.)
A nice thing about this approach is that it helps simplify the app view, which doesn't require unpacking the Data parameter anymore and laying out the sidebar and the contents as the second example illustrates.
We could take this a step further and make the app view obsolete by creating an HTML page container as follows:
html_container_controller.erl:
-module(html_container_controller).
-compile(export_all).
private() -> true.
index(A, Ewc) ->
Ewc.
html_container_view.et:
<% Data %>
<%@ index(Data) %>
My App
<% Data %>
Now, we could implement hook/1 as follows:
hook(A) ->
Ewc = erlyweb:get_initial_ewc({ewc, A}, myapp_erlyweb_data),
{ewc, html_container,
[A, {ewc, sidebar_container, [A, Ewc]}]}.
With this container structure, out app view becomes
<% Data %>
As you can see, using the container approach, we've replaced the somewhat crippled app view with a much more flexible and powerful ErlyWeb component. Not bad, huh?
So where does this lead us? Will app views soon be a thing of the past? I think this may very well happen :)
Thursday, January 11, 2007
ErlyWeb Tutorial: Creating a Simple Login Page
Let's see how to create a simple login component with ErlyWeb.
Update: I simplified the HTML to reduce clutter.
First, let's take a look at login_view.et:
<% Data %>
<%@ index({Username, Errs}) %>
<% [error(Err) || Err <- Errs] %>
login
username:
password:
<%@ error(Err) %>- <% error_msg(Err) %>
<%@ error_msg({value_too_short, FieldName, Min}) %>The <% FieldName %> must be at least <% Min %> characters.
<%@ error_msg(invalid_credentials) %>Invalid username/password.
As you can see, the 'index' function of login_view.et accepts a 2 element tuple. The first element is the value of the 'username' field, and the second element contains a list of error values. An error value can be any Erlang data type. The error_msg/1 function maps error values to their textual message. This is done by simple pattern-matching (aside: damn, I love pattern-matching :) ).
Before we take a look at the controller, let's implement a nice little helper function that contains some boiler-plate code for form validation.
Update: The following is generic, reusable code that can work for any form validation, not just login pages. It may even be included in the framework at some point.
validate(A, Fields, Fun) ->
Params = yaws_api:parse_post(A),
lists:foldl(
fun(Field, {Vals, Errs}) ->
case proplists:lookup(Field, Params) of
none -> exit({missing_param, Field});
{_, Val} ->
Val1 = case Val of undefined -> ""; _ -> Val end,
Acc1 =
case Fun(Field, Val1) of
ok ->
{[Val1 | Vals], Errs};
Err ->
{[Val1 | Vals], [Err | Errs]}
end,
Acc1
end
end, {[], []}, lists:reverse(Fields)).
This function takes the Yaws arg record, a list of expected field names to validate, and a function object that takes 2 parameters -- the field name and the value -- and returns 'ok' or an error code. The validate/3 function returns a 2 element tuple. The first element is the list of values corresponding to the field names, and the second element is a list of error codes returned by the function object as it was applied to each field. validate/3 also converts empty field values from 'undefined' to empty strings so they can be passed to the view component, which always returns iolists, verbatim.
Now, let's take a look at login_controller.erl:
index(A) ->
case yaws_arg:method(A) of
'GET' ->
{data, {"", []}};
'POST' ->
case validate(A, ["username", "password"], fun validate_field/2) of
{[Username, Password], []} ->
case app_user:find(
{'and',
[{username,'=',Username},
{password,'=',crypto:sha(UserName ++ Password)}]}) of
[] ->
{data, {Username, [invalid_credentials]}};
[User] ->
Key = crypto:rand_bytes(20),
Encoded = http_base_64:encode(binary_to_list(Key)),
app_user:save(app_user:key(User, Key)),
{response,
[yaws_api:setcookie("key", Encoded),
{ewr, main}]}
end;
{[Username, _Password], Errors} ->
{data, {Username, Errors}}
end
end.
validate_field("username", Val) when length(Val) > 4 -> ok;
validate_field("username", _) -> {value_too_short, "username", "5"};
validate_field("password", Val) when length(Val) > 5 -> ok;
validate_field("password", _) -> {value_too_short, "password", "6"} .
If the request is a GET, login_controller passes into the view function an empty string for the username and no errors. The it's a POST, login_controller checks that the fields have passed basic validation, i.e. their values pass the minimum length requirements. If so, it checks that the username/password hash combination is in the database. If is it, login_controller assigns the user a random 20 byte key string as a cookie value, which it also saves in the database (in this example, we use the database for session management, but this isn't required) and then redirects to the 'main' component (note: this 'response' tuple is only available in ErlyWeb 0.4, which hasn't been released yet). If the username/password combination is invalid, login_controller passes to the view function the previous Username value and the 'invalid_credentials' error code.
This example illustrates a few cool things about Erlang/ErlyWeb:
- ErlyDB makes working with the underying RDBMS very simple and natural.
- Pattern matching is a joy, both in validation code and in ErlTL templates.
- ErlSQL lets us create short SQL statement snippets dynamically while avoiding manual iolist construction and guaranteeing protection against SQL injection attacks.
- Everything is so easy! You gotta love Erlang :)
Alright, now back to work.
Tuesday, January 02, 2007
My Talk at MSLUG
On January 17th, I'll be giving a talk about my adventures in open source Erlang at the Montreal Scheme/Lisp User Group (MSLUG) meeting. For more information, visit http://schemeway.dyndns.org/mslug.
I hear the members of this group have a strong interest in Erlang. Guillaume Germain, who wrote Termite, an implementation of Erlang style concurrency for Gambit-C Scheme, is a member, and so is Marc Feeley, the author of Gamit-C. I'm looking forward for my second opportunity to be surrounded by people who are interested in Erlang :)
This meeting is open to everyone, so if you're interested in attending, you're more than welcome.
Aside: I lived in Montreal for a year when I was 15. My last visit was about 5 years ago, so It'll be nice to visit again. It's been a while since I've had some good poutine! :)
Saturday, December 30, 2006
New Year's Brain Teaser
Here's a good brain teaser to give you some entertainment for the new year.
You have 12 balls, identical in every way except that one of them has a different weight from all the others. You also have a balance scale. In 3 rounds, in each of which you can compare the weights of any 2 groups of balls, you have to ascertain which is the ball with the different weight and whether this ball is heavier or lighter than the other balls.
This is not a trick question in any way. I solved part of this puzzle after I got a significant hint. I wish I had tried a bit harder to solve it by myself, but it's too late now. If you want a hint, you can email me, but you'll feel greater satisfaction if you solve it by yourself.
Happy new year!
Tuesday, December 19, 2006
ErlyWeb is Undocumented No More!
I resisted the distractions. Even the killer duo of Lord of the Rings books and the World Wide Web couldn't stop me (although it did slow me down considerably :) ). I finally managed to sit down on my ass until I finished writing the first draft of the ErlyWeb API documentation, in meticulous conformance to the edoc specification!
Yes, my friends, it's true. You can now go to http://erlyweb.org/ and feast your eyes on the most glorious API documentation ever written! Well, ok, I admit that may be a slight exaggeration, but it sure beats having to rely on a bunch of tutorials and source code, doesn't it? :)
If you find any glaring holes or errors, please let me know.
(To generate the HTML yourself, check out the latest version from trunk and do the equivalent of "edoc:application('ErlyWeb', "/path/to/erlyweb/trunk", [no_packages])", and the files will appear in erlyweb/doc.)
Enjoy!
Tuesday, December 12, 2006
ErlyWeb Tutorial by Brian Olsen, Part 2
Brian Olsen wrote the second part of his ErlyWeb tutorial. In this part, he shows how to acheive maximum code reuse for editing and creating new postings by making smart use of higher-order functions. It's great stuff! You can read it at http://progexpr.blogspot.com/2006/12/erlyweb-tutorial-part-2.html.
Monday, December 11, 2006
A General Theory of Programming Language Relativity
I don't usually write responses to other articles because, frankly, I think people spend too much time writing articles and too little time writing open source Erlang code that I can use without paying those do-gooder souls a dime for their efforts :) However, this one -- "No Silver Bullet" and Functional Programming -- piqued my curiosity because it discusses functional programming in a positive light and it mentions Erlang a bunch of times. Clearly, the author has gotten *something* right :)
I actually think the author did a pretty good job at explaining some of the benefits of functional programming. The main issue I have with the article is that, as past in other articles I've seen, it puts too much weight on conciseness as an indicator of expressiveness. Although conciseness often contributes to expressiveness, conciseness isn't the only measure of expressiveness. It it were, the commenter who wrote that Python generally has a similar conciseness multiplier to C++ as Haskell, debunking this shootout's conclusion that functional languages are "better" (i.e. more expressive), would have a strong argument.
Both the author and the commenter are making valid points, but I think they are overlooking an aspect of language expressiveness that's at least as important, if not more important, for code quality as conciseness: readability. The reason is pretty obvious: code communicates a solution to a problem not just to computers, but also to humans. If code written in a given language is unduly hard to understand, its conciseness doesn't hold much value because the pain of debugging and maintaining it outweights the ease of writing it. I think it's safe to say that most programmers would prefer to work with a 1500 line solution that's highly readable than a 1000 line solution that's hard to comprehend.
So how does this relate to the functional vs. imperative programming debate? Although functional languages (I'm talking primarily about Erlang because that's the functional language I know best) don't always trump OO/imperative ones in conciseness, their code does generally wins hands down in readability.
This is especially true when comparing functional languages to dynamically-typed OO languages, whose code for anything beyond short scripts is borderline unreadable IMO. Dynamically typed OO languages trade a large degree of readability for conciseness, and that's why Java code, as bloated as it often is, is often more readable than Python and Ruby code. The reason is in the fundamental design (flaw?) of OO languages: they encourage the programmer to bind functions to data (they name the child of this unhealthy marriage an 'object' to make us think this chimera models something from the "real world" :) ), and then use data objects to indirectly invoke bound (virtual) functions. If the type of an object isn't evident in the source code, it's difficult to figure out what functions are called when you read statements such as "obj.doSomething(param)". Due to the abundance of indirect function calls in OO code, dynamically typed OO languages require extra discipline by the programmer to carefully document the types of all variables in his/her code so that other people have a chance to understand what functions are being called.
In my relatively brief encounter with Python, I ran into a bunch of code containing seemingly innocent idioms such as the following (I apologize in advance for Python syntax errors):
respond(channel, msg):
if channel.isValid() and msg.body().equals("next"):
return channel.send("A2-B3")
else:
return channel.send("bye")
Because I has no idea what the types of 'channel' and 'msg' are (the code was poorly documented), what such snippets did was a veritable mystery. After wallowing for hours at a time in such nebulae, trying arduously to trace back to the instantiation points of mysterious parameters, where I would hope to find the golden nuggets of information indicating what types their variables are holding, my frustration would reach such uncomfortable levels that I wouldn't know anymore whether to feel angry or deeply depressed.
When you read Erlang code -- even with scant documentation -- you don't normally have go through such troubles. Although Erlang is dynamically typed, Erlang code avoids such readability black holes because it doesn't throw so much type information out the window. In Erlang, the above snippet would be written as follows:
respond(Channel, Msg) ->
case Channel#chess_channel.is_valid && Msg#chess_msg.body == next of
true -> chess_channel:send(Channel, {move, a2, b3});
false -> chess_channel:send(Channel, bye)
end
The Erlang code is less concise, but it's also more readable. (It's also more optimized because it doesn't require the resolution of function pointers in runtime.) In a large code base, this added readability wins over conciseness because it can make difference between providing continuous service and begging your users to come back in a few more hours as you're chasing the mysterious bug that has taken your system offline. It also helps you develop new features faster because you can spend less time debugging and more time coding.
At this point, you may be thinking, "The extra type information in Erlang code has a cost because it sacrifices generality." If you need to write generic code, you can use Erlang's remote function invocation as follows:
respond(ChannelType, Channel, MsgType, Msg) ->
case ChannelType:is_valid(Channel) && MsgType:body(Msg) == "ok" of
true -> ChannelType:send(Channel, {move, a2, b3});
false -> ChannelType:send(Channel, bye)
end.
This example is admittedly silly, but as you can see, Erlang lets you parameterize module names (and function names) in generic code. This capability is often very useful. Even with remote invocation, the vast majority of Erlang code I've read contains enough type information to be much more readable than imperative/OO code.
In addition to the general absence of mystery functions (for a counter-example, check out ErlyDB :) ), Erlang, and other functional languages such as Haskell, have two language features that make them more readable than imperative languages: pattern-matching and immutability.
How do these features enhance readability and therefore expressiveness? Pattern-matching is easy -- just compare the following snippets:
Erlang:
foo({bar, [baz, boing], "18"}) -> ok;
foo(_) -> error.
Imperative, dynamic pseudo-language:
foo(arr) {
if (arr instanceof Array &&
arr.length == 3 &&
arr[0] == 'bar' &&
arr[1] instanceof List &&
arr[1].size() == 2 &&
arr[1].element(0) == 'baz' &&
arr[1].element(1) == 'boing') &&
arr[2] instanceof String &&
arr[2].equals("18"))
return 'ok';
else
return 'error';
}
Update (12/12/06): If you read Anders' comment, you'll see that code such as above can be written more succinctly in Python. Please take this example as an illustration of how Erlang pattern matching engine works rather than as a suggestion that there's no better way of writing such code in any imperative language.
I hope this example makes the benefits of pattern-matching obvious :) So, let's look single-assignment, which is another functional programming feature that I have learned to appreciate as an essential contributor to code quality. In Erlang, when you bind a value to a variable, this binding holds for the life of the variable. For instance, in Erlang, if you wrote code such as
foo() ->
X = 1,
X = 2.
the second expression would throw an error. If you're used to Erlang, the reason is quite natural: the first line states that X equals 1, and therefore the following line, stating that X equals 2, is wrong. To someone who isn't used to functional programming, the benefits of this behavior may not be obvious -- it may even seem like a burdensome restriction (I used to think so too). However, over time I learned that single-assignment often makes for drastically more readable code. For example, consider this snippet in an imperative/OO/dynamic language:
out(name, paradigm) {
var l = new Language(name, paradigm);
// much code below
...
l = bar(l);
// much code below
...
return l.getName() + "/" + l.getParadigm();
}
Now answer the following question: what does 'out("Ruby", "imperative")' return? Clearly, you have no way of knowing. In fact, even reading the all the code for the 'foo' function won't help you much -- you'd have to read the code for 'bar' (and any other function that take 'l' as a parameter) in order to have a better clue. Sadly, all that reading still wouldn't guarantee anything if the value of 'l' changes during execution based on some IO input. And that's not the end of it: to make things worse, your life would be even much more miserable if the author of the code decided to venture into the dangerous terrain of multi-threaded programming. If 'l' is shared between different threads, your code comprehension efforts would be that much closer to hopeless.
If this code were written in Erlang, the answer would be simple: the function 'foo' returns a string of value "Ruby/imperative". In small code snippets, such as the ones used in most language comparisons, the readability benefits of single-assignment may not be obvious. However, in a large-scale production systems with high availability requirements written by large teams of developers, the ability to answer questions about unfamiliar code segments is essential for both readability and (automated and non-automated) debugging. Erlang was designed to build such systems, so it makes sense that Erlang shuns mutable variables. (I imagine Ericsson would be in a rather uncomfortable position if a portion of England's phone network went offline because some programmer thought to himself, "the variable 'l' holds an object of type 'Language'!" :) )
So where does all this lead us? Is there a precise way of measuring language expressiveness that takes into account both conciseness and readability? Well, after thinking about this stuff for a while, I arrived at an elegant equation for arriving at an objective quantitative measure of a language's expressiveness. Without further ado, here is my equation for the General Theory of Programming Language Expressiveness:
E = C*R^2
where
E is expressiveness
C is conciseness
R is readability
Now I just need to figure out how to factor in the speed of light. If I succeed, it would undoubtedly pave the way for the Nobel :)
Well, I hope I was able to shed some light on why readability is at least as important as conciseness when evaluating language expressiveness, and also why code written in functional languages (primarily Erlang) enjoys greater readability -- and often, conciseness -- than code written in imperative/OO languages. Now consider this: even if imperative/OO languages were just as concise and readable as Erlang, Erlang code would nonetheless have a higher average quality. Sounds bizzare? Maybe, but it's true :) The reason is that no matter how good a language is, bugs always creep into non-trivial systems, and Erlang is the only language I know that has truly effective mechanisms for dealing with defects that do affect live systems. The idea behind Erlang's approach to fault-tolerance is actually quite simple: a crash in one process doesn't bring down the whole system, and furthermore it's detected by a supervising process that's configured with rules telling it what action to take (e.g. restart the process) when a crash does occur. When you've fixed that pesky bug that has been causing intermittent (yet non-catastrophic!) crashes, you can hot swap the new code into the live system without taking if offline. Due to Erlang's fault tolerant design, 1000 lines of Erlang code with 7 bugs are in a sense "better" than 1000 lines of Java doing exactly the same thing and containing an equal number of bugs. Unfortunately, comparisons that count only line numbers don't show this side of the story.
If none of this convinces you that functional languages are worth using, maybe this tip the paradigm balance in your mind: I've never met a programmer who's versed in both functional programming and OO/imperative programming and who prefers the latter.
But maybe I just need to get more friends :)
Tuesday, December 05, 2006
ErlyWeb Tutorial by Brian Olsen
Brian Olsen has written a very nice tutorial showing how to create a simple blog application in ErlyWeb. You can read it at http://progexpr.blogspot.com/2006/12/erlyweb-blog-tutorial.html.
It seems that many developers who haven't used Erlang have the perception that it's only good for scaling and concurrency, so it's great to see other people appreciate Erlang (and ErlyWeb) for one if its greatest strengths: simplicity. He's what Brian wrote:
I hope you are seeing what I am seeing. ErlyWeb has INCREDIBLE POTENTIAL, since it avoids a lot of complexity. Any gaps we found thus far were easily plugged in. Even though ErlyWeb is new, this, so far, blows Rails out of the water in terms of brevity.
This is just what I've been trying to achieve with ErlyWeb. I'm happy to see this validation that my efforts have borne fruit.
When I was in Sweden, Joe Armstrong told me (tongue in cheek) that one thing he likes about the Erlang conference is that being surrounded by like-minded people gives you a strong indication that you're not insane.
Bring those indications on :)
Monday, November 27, 2006
Europe, November 2006
I'm here in Barcelona, where I will stay for a few more days until the end of my trip. I've had a great time, but I can't wait to get back home. It feels like it's time to go back to regular life.
I started in Stockholm, then I went to Copenhagen, Paris, Lyon, Granada, Seville and Barcelona. I spent a few days in most of these cities, where I mostly did a lot of walking around and sight-seeing. I visited a few museums, but I didn't want to spend too much time in museums because I tend to enjoy more wandering around the streets of a new city, soaking in their sights, smells, and sounds.
Seeing a number of artistic creations that are generally regarded as masterpieces has led me to think about where great software stands in the realm of creative endeavors. As in most fields, some software is great and some isn't, but how come most people don't value great software in the same way that they value great music, paintings or architecture? This question can be discussed in great lengths, but I think the answer boils down to the following points: software can only be understood by programmers -- to non-programmers, all code looks like the same gibberish (much code looks like gibberish to coders as well :) ); software is always utilitarian -- practically all software is written as a means to an end, not an an end in itself; software stimulates the left brain exclusively -- it doesn't trigger a gut reaction like other art forms because its appreciation always requires analytical thinking; most people don't care about how an application or a software library is written -- they just want it to work.
So, maybe great software will never be regarded as art, but I think most people would agree that software development is a craft that requires skill and creativity. In addition, all programmers would agree that some code is beautiful and some isn't. I think the field that resembles software the most in the way we regard its creations isn't sculpture, painting, or music, but math. Like software, math is arcane, complex, logical, and most of it is boring, but many mathematical proofs are among the greatest achievements of our civilization.
Ok, enough philosophising -- let's get back to my travels :)
I went on this trip because I had a strong itch to see more of Europe. It's safe to say that this desire is now quite satisfied. By the last week or so of my trip, I had actually gotten pretty tired of traveling. Although the cities I've visited have plenty of unique charm, visiting a sequence of cities for a few days each ends up feeling repetitive. It would probably be more rewarding to stay in one place for a longer time period, where I would get a richer exposure to the local language and culture. But then again, 3.5 weeks probably isn't enough time for a profound cultural experience, anyway. I would have to stay in a foreign country for at least a few months in order to learn the local language and feel a stronger connection to the place and its people.
(It may even require a longer period to make proper Cultural Learnings of Europe for Make Benefit Glorious Nation of U, S and A : ) )
I actually think I've had my fix of city traveling for a while. Looking back at my trips, I have enjoyed nature vacations more than anything, and there are plenty of natural treasures nearby, including many of the US national parks, that I haven't yet visited. Next time, I will try to plan a nice nature adventure.
When I get back home, I will have to plan the next phase of my career. I have a number of ideas for cool Erlang apps, and also a few offers for different kinds of gigs, but I haven't picked a concrete direction yet. I will certainly keep working on ErlyWeb (there are a number of small improvements I will make as soon as I get home), but I think ErlyWeb is quite good as it is and I don't want it to make it bloated by adding too many features to it. (One aspect of ErlyWeb that could definitely use some work is support for additional database drivers in ErlyDB, but I will probably let other developers lead this effort because I don't have a strong need for other drivers right now.) I will also try to build at least one of the apps I have thought up and see how far I can take it. If all goes well, it will become the next YouTube. If not, I hope it will at least merit a line or two on my resume :)
Boston, get ready to make room for one more person :)
Monday, November 13, 2006
Saturday, November 11, 2006
12th Erlang User Conference
Here I am at Stockholm, Sweden, blogging a day after the 12th International Erlang User Conference.
I had a blast.
I arrived here a few days ago after a long journey from Boston via Paris. A few days before I left, Joe Armstrong generously offered to host me. Accepting the invitation couldn't have been any easier -- I don't get to stay very often for 3 days with the creator of my favorite programming language :).
Spending time with Joe was great. We had many interesting conversations about Erlang, computer science, culture, business, politics, design, and life. It was a rare opportunity to befriend someone who has accomplished so much and who has such a wealth of knowledge and ideas about the fields in which I'm most interested.
Joe is passionate about concurrent programming and the design of fault tolerant systems. This is how he summarized the main ideas behind Erlang (I'm probably paraphrasing): "To build a fault tolerant system, you need at least two computers. Why? Because one of them can crash. If you have more than one computer, you need distributed programming. Distributed applications run in different threads, and therefore concurrency is an integral part of fault tolerant systems."
Many of the presentations were very interesting, but the best part (at least for me) was meeting all the Erlangers I knew only from email correspondence, among whom are Claes (Klacke) Wikstrom, Mickael Remond (the founder of Process One), Ulf Wiger, Robert Virding and many others.
Everyone I met was very friendly, intelligent, down-to-earth, and had a healthy sense of humor. They all love programming in Erlang.
I had a great time chatting with Klacke. Klacke has had one of the most impressive careers of anyone I've ever met. He's created Mnesia, Yaws, distributed Erlang, and many parts of the Erlang emulator. He's also one of the people behind Bluetail, Kreditor, and now tail-f. Klacke constantly comes up with great quotes. Here are a couple of good ones as I remember them: "It feels like I have a sharp knife in my pocket, and other people have a blunt one", "Some problems you can't imagine solving if you don't have the right tools".
I also enjoyed talking with Ulf Wiger, who has developed with Joe Armstrong a very interesting framework for cooperative web development in Erlang called Erlhive. Maybe we will have opportunities for collaboration somwhere down the road.
Finally, I had a nice tour of Stockholm today (but too bad it rained). It's a beautiful city. I walked around the old town and I also visited the Vasa museum, whose exhibit is an impressive warship from the 1600's that capsized due to an engineering blunder: to please the the King's whims, the engineers loaded the ship with too many canons. Due to the excess weight at the top of the ship, it had several glorious minutes of sailing before it sank in the Stockholm harbour. Oops.
I would like to write in more detail, but my jetlag is starting to wear thin and I have to get some sleep as I'm flying to Copenhagen tomorrow. After that, I'm travelling for 3 weeks in Europe, visiting Amsterdam, Paris, Lyon, the south of France, and Spain (mostly Barcelona).
I'll end this posting with few pictures I've taken:

Me, Joe and Mickael Remond

Me and Klacke, A.K.A the Erlang Open Source Web Squad. Our weapons: Erlang, Yaws and ErlyWeb. Our mission: to end web development suckage, one webapp at a time :)
Friday, November 03, 2006
ErlyWeb + Yaws 1.64
Some people have had trouble running ErlyWeb on Yaws 1.64. This is because the Yaws 'arg' record has changed. I created a Yaws 1.64 compatible yaws_arg.beam file that you can download here. If you're running Yaws 1.64, drop this file in place of the existing yaws_arg.beam in in your 'erlyweb-0.x/ebin' directory, give it another try, and let me know if it helped.
Thursday, November 02, 2006
New ErlyWeb Google Group
Following the suggestion of one of my readers, I created a Google group for ErlyWeb hackers. You can join it to ask questions, share points of view, help each other out, make suggestions, and announce the launch of the killer ErlyWeb app you're building :)
Here's the link: http://groups.google.com/group/erlyweb
From now on, I will make all announcements about new releases in this group rather than on my blog.
ErlyWeb 0.2
I made some improvements to the first release of ErlyWeb, some based on user feedback, and some based on my own whims :) This is what I did:
- Chaged the LastCompileTime parameter in erlyweb:compile into an option value in the form of {last_compile_time, Time}.
- Added the following options to erlyweb:compile/2:
{auto_compile, true} : this option, useful during development, tells ErlyWeb to compile all files that have changed since the last request when a new request arrives. This frees you from having to call erlyweb:compile every time you make a code change in your app. Just don't forget to turn auto-compilation off by calling erlyweb:compile without the {auto_compile, true} option when you are switching from development to production mode, because auto-compilation slows things down.
Update (12/6/06): The pre_compile_hook and post_compile_hook have changed in ErlyWeb v0.3. Read this announcement for more details.
{pre_compile_hook, {Module, FuncName}} and {post_compile_hook, {Module, FuncName}}: these option tell ErlyWeb to call the predefined functions before/after (auto)compilation. This allows you to extend the compilation process in an arbitrary way, e.g. by compiling additional files that are outside of the application's src directory. Both functions take a single parameter which is the time of the last compilation (or 'undefined' if the time is not available). For example, let's say you have the following file called 'compile_hooks.erl' in the 'src' directory:
-module(compile_hooks).
-compile(export_all).
pre_compile(LastCompileTime) ->
io:format("pre-compile (last: ~p) ~n", [LastCompileTime]).
post_compile(LastCompileTime) ->
io:format("post-compile (last: ~p) ~n", [LastCompileTime]).
You could use the new compilation options as follows:
erlyweb:compile("/path/to/app",
[{erlydb_driver, mysql},
{auto_compile, true},
{pre_compile_hook, {compile_hooks, pre_compile}},
{post_compile_hook, {compile_hooks, post_compile}}]).
From now on, every time ErlyWeb does an auto-compilation, it will call those hooks, passing into them the time of the last compilation (or 'undefined' on the first compilation).
- Changed the include directive in yaws_arg.erl from '-include("yaws/include/yaws_api.hrl").' to '-include("yaws_api.hrl").' (this removes assumptions about your Yaws path structure).
- Changed the docroot directive in yaws from pointing at the application's base directory to [base]/www. E.g., if your previous docroot line was
docroot = /apps/music
it should now be
docroot = /apps/music/www
(there's no change in behavior -- it just makes the configuration more explicit).
That's it :)
Note: a few people have asked me whether ErlyWeb requires a MySQL database. The answer is 'no.' You can use ErlyWeb without any database backend. Just don't keep the source files for any models in src/components, and then you'll never even have to call erlydb:start().
In a minute, I'll put the new zip file on erlyweb.org so you can download it all in one shot.
Wednesday, November 01, 2006
Goodbye, Typo. Hello Wordpress!
Update (12/21/06): I was in a pretty upset state of mind after struggling with a barely-working comment system for many days when I wrote this posting. I didn't want to take my frustrations out on Typo because I liked Typo (plus, I really didn't think this was Typo's fault because Typo worked fine under light load), so I picked on Rails instead. Please read this posting as a silly angry rant rather than a well thought-out criticism.
It wouldn't be fair to judge the venerable Ruby on Rails "platform" based on a single data point (*cough* it sucks! *cough* *cough*), but after days of agony trying to get comment submission to work properly in Typo (executing an INSERT after an HTTP POST must be a requirement that's outside of Rails' "scope"), I decided to go back to my roots and run this blog on Wordpress.
(Yes, I was tempted to write my own blogging engine in Erlang, but I decided against it -- I must keep my eyes on the prize :) )
Based on my admittedly limited Rails experience, even if Erlang isn't your cup of tea, I recommend avoiding Rails and sticking to PHP or Python (or Smalltalk, about which I hear nice things) unless you're an optimization genius who loves the thrill of Linux tinkering; you're hopelessly stricken by a successful marketing blitz; you're a masochist; you wrote a Rails book; or you're just not planning on success.
I can't comprehend why some people think it's justifiable to ask someone how many worker processes he would like to run. I wouldn't want to pick a low number like, say, 5, because that might indicate that I have self esteem issues, but then I'm trapped by the fear than an astronomically high number such as, gosh, 20, would hose my VPS.
Sometimes, when I let my imagination run wild, I wish I could say something crazy, like "500,000".
Oh wait -- I can :)
Update (11/2): Wow! Wordpress is so much faster than Typo! Good job, Matt! :)
Tuesday, October 31, 2006
The Correction
If you have finance background (one of my majors was Economics, the others were Math and Computer Science -- but I liked Math the most, especially probability theory), you should know that markets often price equities irrationally. That's due to the crowd mentality that tends to possess investors: instead of buying the bargain stocks that other investors overlook and that give great returns in the long run, they chase the hyped stocks that get all the news coverage, only to eventually see their prices crash and burn.
Although such bubbles happen periodically, they are always followed by a correction: the market wakes up and realizes that it placed incorrect values on certain stocks, leading to the nose-dive of their prices, whereas other stocks rise from obscurity as investors realize what great values they are.
I think we're finally starting to see a correction in the more interesting field (at least, to me) of programming languages :)
Monday, October 30, 2006
New ErlyWeb Zip file
I created a zip file containing ErlyWeb 0.1 and its libraries for easy installation. Check out erlyweb.org for more info.
ErlyWeb Visitor Map, Day 1
Friday, October 27, 2006
Introducing ErlyWeb: The Erlang Twist on Web Frameworks
If you've been reading this blog for a while and you've been connecting the dots, you probably know where this is going... :) I you haven't, that's ok -- my old postings aren't going anywhere. There's a lot of Erlang goodness in them for you to soak up at your leisure as you're getting up to speed on this great language.
Without further ado, I present to you the culmination of all of my exciting adventures thus far in the land of open source Erlang:
ErlyWeb: The Erlang Twist on Web Frameworks.
Don't worry, I'm not going to blab for a long time now about why I think ErlyWeb + Yaws is the best web development toolkit available (not that I'm biased or anything :) ). Instead, I decided I'll just take you on a quick tour of how to use ErlyWeb, and let you use your own knowledge about Erlang to fill in the gaps :)
- Get the latest ErlyWeb archive from erlyweb.org, unzip it, and put the zip file's contents in your Erlang code path. (The Erlang code path is the root directory within which the Erlang VM searches for compiled modules. In OS X, it's "/usr/local/lib/erlang/lib". For more information, visit http://www.erlang.org/doc/doc-5.5.4/lib/kernel-2.11.4/doc/html/code.html).
- Download and install Yaws if you don't already have it.
- Start Yaws in interactive mode ("yaws -i") and type in the Yaws shell
erlyweb:create_app("music", "/apps").
(I'm assuming that "/apps" is the parent directory of your Yaws apps.)
This will create an ErlyWeb directory structure as well as a few files. (Note: this initial procedure will probably be shorter when ErlyWeb matures.) This is what you should see:
/apps/music
/apps/music/ebin
/apps/music/src/music_app_controller.erl
/apps/music/src/music_app_view.et
/apps/music/src/components
/apps/music/www
/apps/music/www/index.html
/apps/music/www/style.css
- Edit your yaws.conf file by adding a server configuration with the following docroot, appmod, and opaque directives, then type "yaws:restart()."
docroot = /apps/music/www
appmods = <"/music", erlyweb>
appname = music
- Open your browser and point it at http://localhost:8000/music (note: your host/port may be different, depending on your Yaws configuration). You should see the following page, (breathtaking in its design and overflowing with aesthetic genius, if I may add):

- Create a MySQL database called 'music' with the following code (thanks, Wikipedia :) ):
CREATE TABLE musician (
id integer primary key auto_increment,
name varchar(20),
birth_date date,
instrument enum("guitar", "piano",
"drums", "vocals"),
bio text
) type=INNODB;
INSERT INTO musician(name, birth_date,
instrument, bio) VALUES
("John Lennon", "1940/10/9", "vocals",
"An iconic English 20th century
rock and roll songwriter and singer..."),
("Paul McCartney", "1942/6/18", "piano",
"Sir James Paul McCartney
is a popular Grammy Award-winning
English artist..."),
("George Harrison", "1943/2/24", "guitar",
"George Harrison was a popular English
musician best known as a member of The Beatles..."),
("Ringo Star", "1940/7/7", "drums",
"Richard Starkey, known by his stage name
Ringo Starr, is an English popular musician,
singer, and actor, best known as the
drummer for The Beatles...");
- Back in Yaws, type
erlyweb:create_component("musician", "/apps/music").
This will create the following files:
/apps/music/components/musician.erl
-module(musician).
/apps/music/components/musician_controller.erl
-module(musician_controller).
-erlyweb_magic(on).
/apps/music/components/musician_view.erl
-module(musician_view).
-erlyweb_magic(on).
Back in Yaws, type
erlydb:start(mysql, [{hostname, "localhost"}, {username, "username"},
{password, "password"}, {database, "music"}]).
erlyweb:compile("/apps/music", [{erlydb_driver, mysql}]).
(The erlydb_driver option tells ErlyWeb which database driver to use for generating ErlyDB code for the models. Note: this may change in a future version.)
Now go to http://localhost:8000/music/musician, click around, and you'll see the following screens:




"Aha!" you may be thinking now, "I bet he's using some Smerl trickery to call functions that contain mountains of horrible code only comprehensible to Swedish Field Medal winners!"
Well.. um, not exactly. In fact, this is the code for erlyweb_controller.erl
%% @title erlyweb_controller
%% @author Yariv Sadan (yarivsblog@gmail.com, http://yarivsblog.com)
%%
%% @doc This file contains basic CRUD controller logic. It's intended
%% for demonstration purposes, but not for production use.
%%
%% @license For license information see LICENSE.txt
-module(erlyweb_controller).
-author("Yariv Sadan (yarivsblog@gmail.com, http://yarivsblog.com)").
-export([
index/2,
list/2,
list/3,
new/2,
edit/3,
delete/3
]).
-define(RECORDS_PER_PAGE, 10).
index(_A, Model) ->
{ewr, Model, list, [1]}.
list(A, Model) ->
list(A, Model, 1).
list(A, Model, Page) when is_list(Page) ->
list(A, Model, list_to_integer(Page));
list(A, Model, Page) when is_integer(Page) ->
Records = Model:find_range((Page - 1) * ?RECORDS_PER_PAGE,
?RECORDS_PER_PAGE),
%% this function makes the 'edit' links in the record ids
ToIoListFun =
fun(Val, Field) ->
case erlydb_field:name(Field) of
id ->
Id = Model:field_to_iolist(Val),
erlyweb_html:a(
[erlyweb_util:get_app_root(A),
atom_to_list(Model),
<<"edit">>, Id], Id);
_ ->
default
end
end,
{data, {erlyweb_util:get_appname(A),
atom_to_list(Model),
Model:db_field_names_bin(),
Model:to_iolist(Records, ToIoListFun)}}.
new(A, Model) ->
Rec = Model:new(),
new_or_edit(A, Model, Rec).
edit(A, Model, Id) ->
Rec = Model:find_id(Id),
new_or_edit(A, Model, Rec).
new_or_edit(A, Model, Record) ->
Fields = tl(Model:db_fields()),
Vals = tl(Model:to_iolist(Record)),
Combined = lists:zip(Fields, Vals),
IdStr = case Model:id(Record) of
undefined -> [];
Id -> integer_to_list(Id)
end,
case yaws_arg:method(A) of
'GET' ->
FieldData = [{erlydb_field:name_bin(Field),
erlydb_field:html_input_type(Field),
erlydb_field:modifier(Field),
Val} || {Field, Val} <- Combined],
{data, {erlyweb_util:get_app_root(A),
atom_to_list(Model),
IdStr,
yaws_arg:server_path(A),
FieldData}};
'POST' ->
NewVals = yaws_api:parse_post(A),
Record1 = Model:set_fields_from_strs(Record, NewVals),
Model:save(Record1),
{ewr, Model, list}
end.
delete(A, Model, Id) ->
case yaws_arg:method(A) of
'GET' ->
Record = Model:find_id(Id),
Fields = [erlydb_field:name_bin(Field) ||
Field <- Model:db_fields()],
Vals = Model:to_iolist(Record),
Combined =
lists:zipwith(
fun(Field, Val) -> [Field, Val] end,
Fields, Vals),
{data, {erlyweb_util:get_app_root(A),
atom_to_list(Model), Id,
Combined}};
'POST' ->
Model:delete_id(Id),
{ewr, Model, list}
end.
And this is the code for erlyweb_view.et
<%~
%% @title erlyweb_view.et
%% @doc This is a generic view template for making simple CRUD
%% pages with ErlyWeb. It's intended for demonstration purposes,
%% but not for production use.
%%
%% @license for license information see LICENSE.txt
-author("Yariv Sadan (yarivsblog@gmail.com, http://yarivsblog.com)").
-import(erlyweb_html, [a/2, table/1, table/2, form/3]).
%% You can add component-specific headers and footers around the Data
%% element below.
%>
<% Data %>
<%@ list({AppRoot, Model, Fields, Records}) %>
<% a(["", AppRoot, Model, <<"new">>], <<"create new">>) %>
Records of '<% Model %>'
<% table(Records, Fields) %>
<%@ new({_AppRoot, Model, _Id, Action, FieldData}) %>
Create a new <% Model %>:
<% form(Action, <<"new">>, FieldData) %>
<%@ edit({AppRoot, Model, Id, Action, FieldData}) %>
delete
<% form(Action, <<"edit">>, FieldData) %>
<%@ delete({AppRoot, Model, Id, Combined}) %>
Are you sure you want to delete this <% Model %>?
<% table(Combined) %>
method="post">
onclick="location.href='<% AppRoot %>/<% Model%>'"
value="no">
Not exactly the stuff that would win anyone the Field Medal, if I dare say so.
If ErlyDB hasn't convinced you that Erlang is a very flexible language, I hope that ErlyWeb does. In fact, I don't know of any other language that has Erlang's combination of flexibility, elegance and power. (If such a language existed, I wouldn't be using Erlang :) ).
The flexibility of components
The notion of component reusability is central to ErlyWeb's design. In ErlyWeb, each component is made of a view and a controller, whose files are placed in 'src/components'. All controller functions must accept as their first parameter the Yaws Arg for the HTTP request, and they may return any value that Yaws accepts (yes, even ehtml, but ehtml can't be nested in other components). In addition, they can return a few special values:
{data, Data}
{ewr, FuncName}
{ewr, Component, FuncName}
{ewr, Component, FuncName, Params}
{ewc, A}
{ewc, Component, Params}
{ewc, Component, FuncName, Params}
So what do all those funny tuples do?
{data, Data} is simple: it tells ErlyWeb to call the corresponding view function by passing it the Data variable as a parameter, and then send result to the browser.
'ewr' stands for 'ErlyWeb redirect.' The various 'ewr' tuples simplify sending Yaws a 'redirect_local' tuple that has the URL for a component/function/parameters combination in the same app:
- {ewr, FuncName} tells ErlyWeb to return to Yaws a redirect_local to a different function in the same component.
- {ewr, Component, FuncName} tells ErlyWeb to return to Yaws a redirect_local to a function from a different component.
- {ewr, Component, FuncName, Params} tells ErlyWeb to return to Yaws a redirect_local to a component function with the given URL parameters.
For example,
{ewr, musician, list, [4]}
will result in a redirect to
http://localhost:8000/music/musician/list/4
'ewc' stands for 'ErlyWeb component.' By returning an 'ewc' tuple, you are effectively telling ErlyWeb, "render the component described by this tuple, and then send the result to the view function for additional rendering." Returning a single 'ewc' tuple is similar to 'ewr', with a few differences:
- 'ewc' doesn't trigger a browser redirect
- the result of the rendering is sent to the view function
- {ewc, Arg} lets you rewrite the arg prior to invoking other controller functions.
(If this sounds complex, don't worry -- it really isn't. Just try it yourself and see how it works.)
Now to the cool stuff: not only can your controller functions return a single 'ewc' tuple, they can also return a (nested) list of 'ewc' tuples. When this happens, ErlyWeb renders all the components in a depth-first order and the sends the final result to the view function. This lets you very easily create components that are composed of other sub-components.
For example, let's say you wanted to make blog sidebar component with several sub-components. You could implement it as follows:
sidebar_controller.erl
index(A) ->
[{ewc, about, [A]},
{ewc, projects, [A]},
{ewc, categories, [A]},
{ewc, tags, [A]}].
'sidebar_view.et'
<%@ index(Data) %>
What a sidebar!
<% Data %>
Pretty cool, huh?
If you don't want your users to be able to access your sub-components directly by navigating to their corresponding URLs, you can implement the following function in your controllers:
private() -> true.
This will tell ErlyWeb to reject requests for private components that come from a web client directly.
Each application has one special controller that isn't part of the component framwork. This controller is always named '[AppName]\_app\_controller.erl' and it's placed in the 'src' directory. The app controller has a single function called 'hook/1', whose default implementation is
hook(A) -> {ewc, A}.
The app controller hook may return any of the values that normal controller functions return. It is useful for intercepting all requests prior to their processing, letting your rewrite the Arg or explicitly invoke other components (such as a login page).
Well, that's about it for now :) I'll appreciate any feedback, bug reports, useful code contributions, etc.
Final words
After reading all this, some of you may be thinking, "This is weird... I thought Erlang is some scary telcom thing, but what I'm actually seeing here is that Erlang is very simple... Heck, this stuff is even simpler than Rails. What's going on here?"
If that's what you're thinking, then you are right. Erlang *is* simpler than Ruby, and that's why ErlyWeb is naturally simpler than Rails. In fact, Erlang's simplicity is one of its most underrated aspects. Erlang's creators knew very well what they were doing when they insisted on keeping Erlang simple: complexity leads to bugs; bugs lead to downtime; and if there's one thing Erlangers hate the most, it's downtime.
Subscribe to:
Posts (Atom)