Thursday, December 21, 2006

People are actually reading this?

Hmm, I never drew any popularity to any of my prior silly weblog attempts until now.It must be the tutorial that I put up. :D

Thanks for all the comments thus far.

Tuesday, December 19, 2006

OO That Looks Like Procedural Programs

I wrote this to a couple of people on a private mailing list I am on:

"OO has never lived up to the potential for me, for precisely the
reason that most 'OO type' work I do (mostly via web apps) all tends to look like
procedural applications just with OO constructs around it. Get rid of
the OO constructs, and you pretty much got the same program, just
without the OO overhead. So why should I bother with OO? This is one
of the motivations that led me to functional programming. All the good
practices that is present OO still stand (like hiding implementation
from interface, the use of opaque values (related with the first one),
polymorphic functions), but it just doesn't think it is necessary to
bind data and bahavior together. Higher-order functions seem, right
now, to make sense more, for reusability purposes now than through
inheritance (ask my opinion on it after I developed some non-trivial
stuff with functional languages :))"

I worked in Rails and I played with Python a lot in these areas. My belief stems from this change in perspective:

Objects in Python are glorified dictionaries, or dictionaries with OO logic. If inheritance is necessary, then the OO logic is useful. But, eh ... I don't use inheritance. At most, I use tables to inherit similarities, using joins to accomplish that type of goal. I don't know what other people do, but I never used inheritance in such a grand way to actually see a use for it.

So, looking at something like ErlyDB, and its simple use of tuples, confirmed my point. I just got lazy a little.

The question is: is there anything special when it comes to merging data and functions together. For what I do, I just can't see it.

I said in the first tutorial that ErlyWeb (+ erlhive, but I haven't had a chance to look at that yet) is a Rails killer. Let me elaborate on that. It might not be flush with so many features as Rails does, but the constructs are much more simpler in ErlyWeb. The thing is that ErlyWeb is not this big opaque library of magic tricks; it doesn't need to be, no matter how many "opinions" are shoved into it.

But I think this pretty much reflects not so much the capabilties of a language, but the opinion of the language community. Where Rails says "you are stuck with the whole methodology that Rails has to offer", ErlyWeb should say "here is a few interesting tools to get you moving - other than that, everything is pretty much intuitive after that" This is why I think it could draw people to Erlang. I like that.

Monday, December 11, 2006

ErlyWeb Tutorial Part 2

I have been looking forward to getting back to adding to this tutorial, but I have not had the chance the past few days. Now that I have a little time, I can jump in and finish the tutorial. What this part of the tutorial will address is the edit functionality for our blog application. Taking a hint from the CRUD functions that is in ErlyWeb, I will do something similar.

(Tip for Erlang newbies: use m(module_name) to find out about what functions are exported from a module. Very handy feature!)

ErlyDB looks like a smart fellow, because it knows that, despite using basic tuples, can figure out if the data is in the database or not. It probably isn't anything special, and Yariv will probably agree (since he wrote it), but it is interesting nonetheless.

I said this for a reason. We can similify our code, transforming the functionality in new_post/1 into something that calls a generic function, new_or_edit/2, which will also handle post editing. This is possible because ErlyDB saves records with same function.

In your entries_controller.erl file, get rid of the new_post/1 function, and then stick this in:


new_post(Vals) ->
Entry = entries:set_fields_from_strs(entries:new(), Vals),
new_or_edit(Entry, Vals).


We exported most of the logic that was in new_post/1 into new_or_edit/2. Put the new_or_edit/2 function in:



new_or_edit(Entry, Vals) ->
EntryV = entries:set_fields_from_strs(Entry, Vals),
Errors = helpers:validate(entries_validate, entries, EntryV),

case Errors of
ok ->
entries:save(EntryV),
{ok, EntryV};
_ ->
{Errors, EntryV}
end.



It is a fact that this function applies to both creating a new entry and editing an entry. This is true because what we are going to show now, with the edit functionality. Put this code in:


%% Edit functions.

edit(A, Id) ->
case yaws_arg:method(A) of
'GET' ->
{data, {"", edit_get(Id)}};
'POST' ->
Vals = yaws_api:parse_post(A),
{Errors, Entry} = edit_post(Vals, Id),
case Errors of
ok ->
{ewr, entries, index};
_ ->
{data, {Errors, Entry}}
end
end.


edit_get(Id) ->
Entry = entries:find_id(Id),
Entry.

edit_post(Vals, Id) ->
Entry = entries:find_id(Id),
new_or_edit(Entry, Vals).



WAIT A MINUTE! We are doing almost the same thing that is being done in our new function! Time to do some parameterization, to get rid of that look of cut-and-paste yuckiness. Look at where the differences are ... The function that is called to get an entry in "GET" and possibly, the space where we do a redirect. Let's add a library function! But, for you neophytes to functional programming ... we are going to use higher-order functions.

Here is the generic function:



form_process(A,
GetAction,
ModelAction,
PostSuccessAction,
PostFailureAction) ->
case yaws_arg:method(A) of
'GET' ->
GetAction();
'POST' ->
Vals = yaws_api:parse_post(A),
{Errors, Record} = ModelAction(Vals),
case Errors of
ok ->
PostSuccessAction();
_ ->
PostFailureAction(Errors, Record)
end
end.


Essentially, the form_process/5 function is just a generalization of what we have been doing before. Except, this time, we replaced the specific places with function calls - to functions we will define in our new/1 function.


new(A) ->
GetAction = fun() ->
{data, {[], new_get()}} end,
ModelAction = fun(Vals) ->
new_post(Vals) end,
PostSuccessAction = fun() ->
{ewr, entries, index} end,
PostFailureAction = fun(Errors, Entry) ->
{data, {Errors, Entry}} end,

form_process(A, GetAction, ModelAction, PostSuccessAction, PostFailureAction).


and then edit/1:


edit(A, Id) ->
GetAction = fun() ->
{data, {[], edit_get(Id)}} end,
ModelAction = fun(Vals) ->
edit_post(Vals, Id) end,
PostSuccessAction = fun() ->
{ewr, entries, index} end,
PostFailureAction = fun(Errors, Entry) ->
{data, {Errors, Entry}} end,

form_process(A, GetAction, ModelAction, PostSuccessAction, PostFailureAction).



(NOTE! For some weird reason, when I am running new/1 through the browser, and hit submit, it takes a bit too long to load. Is this a bug in my code? edit/1, though, works like a charm. Go figure.)

So, we developed a pattern using common code and, more importantly, a common convention, meaning a common set of expectations. There is nothing grandiose about the convention, and yes, you can completely ignore it. The lines of code in my function is not my concern, but whether or not I can make the code more declarative, in the sense that I say "do this in this location, do this in this other location." But, I am thinking that the code there can be tightened up even more.

In your entries_view.erl file, add these functions:


new(Data) ->
entry_form:display_form(Data, "../new", "").

edit(Data) ->
entry_form:display_form(Data, "../edit",
integer_to_list(entries:id(element(2,Data)))).


Yep, we are using the same form for editing and displaying. However, we made a change. I needed to make the action in the form generic. So the new parameters represent the url to go to. I moved the form to its own module, in a file called "entry_form.et" - I am going for some sense of template organization, by putting one page per ErlTL file. But, I originally put it in "entries_show.et". Note the changes I made with the parameters. Here is the new lines:

<%@ display_form(Items, GoTo, Id) %>

and

< form action="<% GoTo %>/<% Id %>" method="POST" >


We also want to make a small addition, by adding a "view" function. This one is quite simple. In your entries_controller.erl file, add this function:


view(A, Id) ->
Entry = get_entry(Id),
{data, Entry}.


In your entries_view.erl file, add this function:


view(Data) ->
entry_view:view(Data).


Add a file called "entry_view.et" for the view, and add this to the file:


<%@ view(Data) %>
< b><% entries:title(Data) %>< /b>
< br />< br />
<% entries:body(Data) %>
< br />

< i>by: <% entries:author(Data) %>< /i>
< br />< br />
<% erlyweb_html:a(["../edit", integer_to_list(entries:id(Data))], "Edit Entry") %>


Maybe, also, add a link to the view in your entries_show.et file.


<% erlyweb_html:a(["view", integer_to_list(entries:id(Entry))], "view") %>


Put it in your entry/1 function in the template.

Also, maybe you can finish off the show_entries/1 function in the same template file with a link to the new entry form. I'm lazy - you do it yourself. :)

Now, go to Yaws, type "start:boot()" and go to your index page. All of the elements should be visible. Is it? Any problems? If there is, this is a good time to learn what Erlang is saying to you. :P Now, if there is a problem in the tutorial, do contact me.

Monday, December 04, 2006

ErlyWeb - Blog Tutorial

UPDATE: I am going to expand this tutorial a bit, later on, but, as of now, the tutorial can stand on its own. I only included a discussion on displaying blog entries and creating new entries. Later, I will be adding a discussion on adding an edit page and adding delete functionality.

A week ago, I started to play with Erlang. I wanted to look into web development with functional languages, so I invested time in a certain toolkit called ErlyWeb.

Here is a small tutorial of creating a simple weblog application in it. I am assuming you went through the musician tutorial, which explains in detail, getting Erlang, ErlyWeb, MySQL, blah blah blah. This tutorial is just a rough sketch of discoveries I made while using it.

(I typed this really quickly. So, if you try this, you need to fix all the HTML ... Blogger was rendering the HTML and I am too tired to find out why. So, I merely stuck a space after each < for now. I'll fix later.)

To start, we need to create an application like such, in the erl shell:


erlyweb:create_app("blog", "/path/to/apps").


Then, we create a table in MySQL, like such:


create table entries (
id integer auto_increment primary key,
title varchar(100),
body text,
author varchar(100)
);


We create an entries component:


erlyweb:create_component("entries", "/path/to/apps/blog").


While here, create a file called "entries_validate.erl" in your "src" directory in your app directory.

I created a small file that I put on the root of the blog directory which will start up a MySQL connection in ErlyDB as well as compile the whole app. I put this file on the root of the blog directory (and will reference to this place in other places in the tutorial.) Here is the file (edit where necessary):


-module(start).
-export([boot/0, boot/1]).


boot() ->
boot(true).
boot(false) ->
compile();
boot(true) ->
mysql_start(),
compile().

mysql_start() ->
erlydb:start(mysql, [{hostname, "localhost"},
{username, "username"},
{password, "password"},
{database, "blog"}]).

compile() ->
erlyweb:compile("/path/to/app/blog",
[{erlydb_driver, mysql}]).


This comes in handy, if you restart yaws, for example - you don't need to type in the connection function every time.

Edit your yaws.conf file:


< server junk >
port = 8000
listen = 127.0.0.1
docroot = /path/to/blog/www
appmods = <"/blog", erlyweb>

appname = blog

< /server >


Then, start yaws:


yaws -i


Cruise over to your localhost:8000/blog/entries page, and enter a few new entries, for some starter data.

In your src/components/entries_controller.erl file, comment out the erlyweb_magic line. We are turning off the stuff we were using just before.

Now, we will start with the front page. We will want to create an index function that merely shows all the entries on the screen. In the entries_controller.erl file, enter this function in:


index(A) ->
Entries = entries:find(),
{data, Entries}


and put in your export line:


-export([index/1]).


What that function is merely saying is that it should get all the entries in the the entries table, and send the entries off to the view function.

Open up entries_view.erl, and enter this:


index(Data) ->
entries_show:show_entries(Data).


and in your export line:


-export([index/1]).


In our view function, we are getting the data ("Entries") from the controller function, and now we are merely passing the data to the display function. Make a file called "entries_show.et" in the src/components directory and enter this code in:


<%@ show_entries(Entries) %>

<% [entry(E) || E <- Entries] %>


<%@ entry(Entry) %>

<% helpers:value(entries:title(Entry)) %>



<% helpers:value(entries:body(Entry)) %>


by: <% helpers:value(entries:author(Entry)) %>





We are almost done. In the current version of ErlyWeb, I was having problems when the value of a particular field, like the output from entries:title(), would return undefined, and it would crash the yaws process (of course, it is not going to crash the server!) So, I created a small set of functions in a "helpers" module. Make this file in your src directory, and add this code:


-module(helpers).
-export([value/1]).

value(Val) ->
case Val of
undefined ->
"";
_ ->
Val
end.


We'll use this little code snippet for now.

Now, go to the root of the blog directory (you can use the cd() function in yaws, so you don't have to go back to your shell) and type:


start:boot().


Hopefully, everything will be fine. It will finish up with a tuple starting with "ok" at the beginning.

Go to your page, http://www.localhost:8000/blog/entries and you will hopefully see, in all its glory, the records you entered in before.

Now, we are going to expand the helpers module. Here is the complete helpers module file:


-module(helpers).
-export([value/1, validate/3]).

value(Val) ->
case Val of
undefined ->
"";
_ ->
Val
end.

validate(ValidatorModule, Model, Item) ->
Fields = Model:use_fields(),
Results = [ValidatorModule:Field(Model:Field(Item)) || Field <- Fields],
Errors = [Error || Error <- Results,element(1, Error) == error],
case Errors of [] ->
ok;
_ ->
Errors
end.


We added a validation function here. We are going to add a simple form to the mix; something to add new entries.

Back in the entries_controller.erl file, enter in this code:


new(A) ->
case yaws_arg:method(A) of
'GET' ->
{data, {[], new_get()}};
'POST' ->
Vals = yaws_api:parse_post(A),
{Errors, Entry} = new_post(Vals),
case Errors of
ok ->
{ewr, index};
_ ->
{data, {Errors, Entry}}
end
end.


new_get() ->
Entry = entries:new(),
Entry.

new_post(Vals) ->
Entry = entries:set_fields_from_strs(entries:new(), Vals),
Errors = helpers:validate(entries_validate, entries, Entry),
case Errors of
ok ->
entries:save(Entry),
{ok, Entry};
_ ->
{Errors, Entry}
end.


and our export line:


-export([index/1, new/1, new_get/0, new_post/1]).


(We could make the new_get/0 and new_post/1 functions private to the web, but we'll consider this later.)

The new/1 function is the function that is exposed to the web. I split the logic of supplying a new form and creating a new entry because it will prove easier to unit test instead of mucking around with making a value to submit into new/1.

For GET in new/1, we merely drop in a new entries tuple into the form:


'GET' ->
{data, {[], new_get()}};
...

new_get() ->
Entry = entries:new(),
Entry.


I return from new/1 in the 'GET' case with a "data" tuple, with a 2-tuple. The first item in the 2-tuple is an empty list - for the 'POST' part, we would use that location in the 2-tuple for errors. The second location is the entry we are considering.

For 'POST':

'POST' ->
Vals = yaws_api:parse_post(A),
{Errors, Entry} = new_post(Vals),
case Errors of
ok ->
{ewr, index};
_ ->
{data, {Errors, Entry}}
end


Here, we get all the values from the post payload, and then run the new_post/1 function. If new_post/1 returns {ok, Entry}, then it will redirect to the index page. If not, it will show the form again, and display the errors, and fill in the form elements that are already filled in. Here, we can see now the corollary between the return value in 'GET':


{data, {Errors, Entry}}


"Errors" contains a list of tuples containing our error messages. The Entry variable contains an ErlyDB tuple that we received from new_post/1. Let's look at new_post/1.


new_post(Vals) ->
Entry = entries:set_fields_from_strs(entries:new(), Vals),
Errors = helpers:validate(entries_validate, entries, Entry),
case Errors of
ok ->
entries:save(Entry),
{ok, Entry};
_ ->
{Errors, Entry}
end.


Here, when we get the values from the post payload, we convert the values into an entries tuple. Then, we submit into our validate/3 function (from before) with any errors. If validate/3 returns ok, then there were no errors. Otherwise, we will return the errors.

validate/3 takes the name of your validation module, your model module and the actual data tuple.

I said to create an entries_validate.erl file. Let's create that.


-module(entries_validate).
-export([title/1, body/1, author/1]).

title(undefined) ->
{error, title, "The title field is blank."};
title(_) ->
ok.

body(undefined) ->
{error, body, "The body field is blank."};
body(_) ->
ok.

author(undefined) ->
{error, author, "The author field is blank."};
author(_) ->
ok.


In entries.erl, put this:


-export([use_fields/0]).

use_fields() ->
[title, body, author].


The validate/3 function loops through the list of names from use_fields() and checks the data in the model tuple, using the functions you provide in entries_validate.erl.

(I say, isn't validation extremely simple in ErlyWeb? A tiny function to get rid of boilerplate code, and a small convention on how to present the data, and that's it!)

Once you got validation in place, enter this into your entries_view.erl file:


new(Data) ->
entries_show:display_form(Data).


and the export line:


-export([index/1, new/1]).


and now, the form, which we will stick also at the bottom of entries_show.et:


<%@ display_form(Items) %>
<%? {Errors, Data} = Items %>
<% helpers_html:show_errors(Errors) %>

< form action="new" method="post">
< table>
< tbody>< tr>< td valign="top">Title:< /td>
< td>
<% erlyweb_html:input("title", text_field, undefined, helpers:value(entries:title(Data))) %>
< /td>
< /tr>

< tr>< td valign="top">Body:< /td>
< td>
<% erlyweb_html:input("body", text_area, undefined, helpers:value(entries:body(Data))) %>
< /td>
< /tr>

< tr>Author:< /td>
< td><% erlyweb_html:input("author", text_field, undefined, helpers:value(entries:author(Data))) %>< /td>
< /tr>
< tr>< input value="Submit" type="submit">< /td>< /tr>
< /tbody>< /table>
< /form>


Hopefully, all this code is self-evident.

Before we finish, one more thing. I added a small module in ErlTL called helpers_html.et in my src/ directory:


<%@ show_errors(Errors) %>
< ul>
<% [error(E) || E <- Errors] %>
< /ul>

<%@ error(Error) %>
< li><% element(3, Error) %>< /li>


This will show us our errors, if we make a mistake in our form.

Now, back in yaws, type in:


start:boot().


and go back to your browser and go to localhost:8000/entries/new.

Make a mistake. Hit submit. Do you see the errors pop up? Fill in the form correctly. Does it go to the index page properly? If so, we have a simple blog app working.

Of course, I totally ignored editing posts in this tutorial. Maybe I will add this later. But, 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. Of course, it took me longer to figure this all out how to do this, since the docs are not up to par yet. Hopefully, this can be fixed soon.