Sunday, February 23, 2014

Records and HRL Files

Looking online I noticed that there is a lack of good "clear" examples on how to define records inside of hrl files (and use them). Seems like all of the examples have a slight twist. Hopefully these instructions help the beginners out.

The hrl files are used for defining macros and records. I don't know anything about macros [yet]; records are basically fancy tuples where you can "name" values. I'm putting together some page properties for a website I want to build and I want to put all of the display information into one place. I could use tuples and do the following:

-module(page_properties).
-export([all_props/0]).

all_props() -> 
    [
         { bgcolor, "#444444" },
         { bgimage, "someimage.jpg" },
         { h1, "<h1>" }
   ].

Then I would use the all_props() definition to obtain information about what the background color is, or how to define a heading region.This is all well and good- however it forces the users to know the exact tuple layout or it forces us to create accessor functions. Here's an example of how I've done accessors to grab values from tuples:

-module(page_properties).
-export([all_props/0, get_property/1]).

all_props() -> 
    [
         { bgcolor, "#444444" },
         { bgimage, "someimage.jpg" },
         { h1, "<h1>" }
   ].

get_property(Name) -> 
    get_property( all_props(), Name).

get_property( [H | T], Name) ->
    { CurrentName, Value } = H,
    if 
        CurrentName =:= Name -> Value,
        true -> get_property( [], Name )
    end;
get_property( [], Name ) -> 0.


Whew, that's a lot of code to try and obtain property values. All that is left is to access the correct property (from inside the shell):

page_properties:get_property( bgcolor ).

This code isn't too bad- but why would you implement this mechanism when Erlang already has a facility built-in that could do it better? That's where records come in. First off, let's create an "hrl" file to define our record. This is a new example, disregard everything above!

Inside of page_properties.hrl:

-record( page_props,
     {
         bgcolor = "#444444",
         bgimage = "background.jpg"
    }).

Now inside of html_body.erl:

-module(html_body).
-include("page_properties.hrl").
-export( [get_bgcolor/0] ).

get_bgcolor() -> #page_props{ bgcolor = Value } = #page_props, Value.

That's all there is to it! Alternately you can define get_bgcolor as:

get_bgcolor() -> A = #page_props{}, A#page_props.bgcolor.

Not sure which one is preferable. If you are trying to get more than one value back from your record, I'm assuming that the pattern-matching mechanism works best. 

Please note the order in which -module, -include, and -export occur. The -include must be done after -module, otherwise you'll get an error stating that a module hasn't been defined! Notice as well that the include takes a string argument to an actual hrl file- this seems different than every other directive I've encountered thus far. You can actually use absolute paths and environment variables here! Pretty cool stuff.

The benefit to defining your code in a record inside of an hrl file is as follows:

- One place for all definitions that is clear and concise.
- No need for memorizing the layout of a tuple.
- No need for fancy code to traverse a tuple to extract values.


        

No comments:

Post a Comment