Friday, November 16, 2007

LilyPond

I've always wanted to learn to use LilyPond. My first two attempts are Mosquito Byte Rag [link fixed] (PDF, 148K) and a piano version of the Equal Rites Main Theme (PDF, 117K). There are no dynamic or note markings yet.

I wrote Mosquito Bite Rag thirty years ago. Thirty. Years. Ago. That makes me feel old, but what's worse is that I haven't written anything seriously in over ten years. Even the Equal Rites theme is around fifteen years old.

JavaScript Crashing Mac OS X Firefox 2.0.0.9

Recently I upgraded to Firefox 2.0.0.9 on Mac OS X. Ever since then, Firefox has been crashing extremely frequently. Most often, it's when I'm closing a tab---usually GMail.

When the Apple problem reporter app fires up, I get to see the stack. Every time I've checked, the crash has been in the JavaScript engine somewhere (the times I've remembered to check, it's always crashed in libmozjs.dylib).

I just reproduced the problem frighteningly easily: after a crash I restarted Firefox. I then went to GMail. After the page loaded, I closed the single tab containing GMail. Crash. Ouch.

What is keeping me from switching to Safari or Opera? At this point, familiarity and Google's bookmark synchronizer. The synchronizer has its own problems, though.

Friday, November 2, 2007

Gmail Adds "Delete" Keyboard Shortcut

Today my GMail account was upgraded to the new interface. The contacts management interface has been improved, but that's not what I'm excited about.

On a whim, I decided to review the keyboard shortcuts found at http://mail.google.com/support/bin/answer.py?answer=6594. Guess what? Google has added "#" as a keyboard shortcut for "delete". YAY! They've also added a bunch of other keyboard shortcuts I haven't seen before: mark as (un)read, archive and previous/next, and undo.

As a die-hard keyboarder, the lack of a "delete" shortcut has been most annoying. I've been waiting for this ever since GMail launched.

Thanks, Google.

Monday, October 8, 2007

Halloween Emacs Tips

Here in time for Halloween: a new section on my Emacs tips page about skeletons.

Tuesday, August 7, 2007

Yaws and Mnesia

I'm playing with translating a small Web application from Rails to the Erlang web app framework Yaws. When I tried to access a Mnesia database from a page, it failed because Mnesia wasn't running. Oops. I tried running it from another erl shell, but that didn't work.

After asking on erlang-questions, here's what I've learned from the responses: You can tell Yaws about Mnesia by using the command line switch '--mnesiadir full-path-to-mnesia-directory' (but that only works when yaws is running as a daemon).

Or, you can connect to the yaws (erl) runtime and start Mnesia from there. If you started Yaws with -sname and cookie you can connect to it like any other Erlang runtime.

Finally, the simplest way to do this is to run yaws from the command line and start mnesia from within the yaws session. That's what I've done.

Thanks to the members of erlang-questions for their help.

Wednesday, July 18, 2007

Erlang Notes

Two Erlang-related notes, neither about the language per se. First, take a look at "Why do you like Erlang?". I love the Magic card. The article's good, too.

Second, my Erlang blog entries for June were selected as that month's contest winners by Pay Eyler of On Erlang, PragDave Thomas, and Joe Armstrong. Thanks to all of them for the honor. There has been no official announcement (and there might not be), but my copy of Programming Erlang is on its way now that the paper edition is shipping. I've had my PDF version for quite a while, and I highly recommend the book.

Wednesday, June 27, 2007

Star Ship Dave

Occasionally movies, TV shows, and ads are filmed around where I work in NYC. I've never seen a "star", but I often see the food trailers and office trailers and once or twice I've seen locations with cameras and lights—no action yet, though.

For a few weeks last fall, an extras tent was set up on the street where I work. The costumes on the extras were really cool: modern, colorful versions of '30s suits and dresses.

Today I saw a catering setup. Nothing odd about that. What was odd was the sign hand-written on a whiteboard attached to the food truck: "Welcome, Star Ship Dave". I can't help chuckling every time I think of that title. Whether or not it's a working title, it's got me wondering. Who is Dave? Is it the ship?

Perhaps it's a Dave we know already: David Hasselhoff, Dave Chapelle, David Letterman, Dave Barry, or David Bowie. (Nah, not Bowie—some Davids are always "David", never "Dave".)

Perhaps I'll brave the 90-degree heat today and venture outside in a while. I want to see what the costumes look like.

Saturday, June 23, 2007

Erlang Boids Simulation Design

As one of my Erlang programming exercises, I decided to write the non-graphical part of a Boids simulation. I've written the same thing in a few different languages (Java, Ruby, Lisp, C++) before. Using Erlang would be interesting because of its support for message-based parallelism and concurrency.

I figured at first that each bird should run in its own process. In each of my other implementations there is also a flock object. Not necessarily a big-O Object, but at least some central place where the birds are collected that can answer questions about them such as the average velocity of the flock and its center of mass. Even with a multi-process Erlang implementation, it makes sense to have a flock process. Flocking behavior rules depend upon each bird's knowledge of the position and velocity of all other birds in the flock (more realistically, all other birds it can see). There are two choices: make each bird know about all other birds or have a flock object that can answer questions about all birds. Making each boid (bird) aware of all the others seems to be a bad design decision: it requires O(n2) communications overhead for every calculation. Each boid has to ask all the others its position and velocity all the time.

With a flock object, the same communications are O(n) because the flock can ask each boid for its position and velocity, and then respond with the aggregate information (average positions, average velocities) when asked.The number of calculations is still O(n2) because each boid wants to know the average position and velocity excluding itself. It's the number of messages and the number of connections between processes that is O(n). To reduce the number of calculations to O(n), you could ignore the "excluding itself" qualification.

My first Erlang implementation resulted in a nasty deadlock. The flock periodically messaged each boid, telling it to move. When the boid received the move message, it updated its position and velocity. In order to do that, it had to message the flock, asking for information about the other boids. When that happened, the program hung because the flock not only wasn't listening for messages, it was still waiting for a response from that boid. Deadlock!

The next version had the flock asking each boid for its location information first, and then telling each boid to move, pasing in the information it needed. This worked, but it was synchronous and definitely not elegant.

Finally, I hit upon a better design: The flock would request position information from each boid periodically, and separately each boid would peroidically tell itself to move, asking the flock for the other boid's information when it needed it. There is a chance that a boid could have moved while other boids were relying upon it's old position and velocity cached by the flock, but that's not really important. In real life birds using old, slightly out-of-date information all the time: they blink, or are distracted by a bug, or are subject to the speed of light's limitations on information gathering.

Firefox: Of Thee I Sing, Thunderbird: Of Thee I Hum

or: Don't Fence Me In
or: Freedom, by George Michaels

I've tried the new Safari Beta 3. It's really fast and refreshingly standards-compliant. I don't care for the brushed aluminum look, though supposedly that's going away.

There's no way I'll switch from Firefox, though. There are two main reasons: I rely on many useful Firefox extensions, and Firefox is still more cross-platform than Safari. Firefox also supports more search engines in its search text field. To be fair and balanced, I must point out that Firefox 2.X crashes or hangs on Mac OS X all the time. I put up with that because of all the other benefits.

That first reason is the biggest though. Firefox is more hackable, which makes it more useful. Here is the list of Firefox extensions I use. The Firefox Add-ons page is also a great resource.

"It's All Text!" lets you edit any textarea's text using your favorite editor. Mine's set to use Emacs, of course. You can set the editor in the preference dialog box, but it might be easier for you to edit the Firefox's user prefs Javascript file and add

user_pref("extensions.itsalltext.editor",
          "/Applications/Emacs.app/Contents/MacOS/Emacs");

I should probably use emacsclient instead so the file is open in the currently running Emacs instead of in a new instance.

Switching to another browser like Safari would force me to give up too many extensions that I have come to rely on. Google Browser Synch is probably the most useful to me, with Firebug and LiveHttpHeaders coming in a close second and third.

Safari does have some features that Firefox doesn't, of course. Sharing your bookmarks with those around you via Bonjour and integration with Apple's AddressBook are the two I can think of. I don't want either. How often have I wanted to share all my bookmarks with others or see all of theirs? Never. Hell, I password-protect my Firefox so that if other people use it they won't see the form field values that Firefox saves for me.

There are some little things that bug me about Safari, too. For example, the keyboard shortcut to open/close the bookmarks sidebar is Option-Command-B and the command to switch to the Google search text field is Option-Command-F. I hate the Option-Command combo; it forces me to switch my whole left hand position away from the home row. In Firefox it's Command-B and Command-K for Mac OS X, Control-B and Control-K for Windows and Linux.

For similar reasons, I use Thunderbird instead of Apple's Mail. Not because of the extensions (I only use one: Enigmail), but because it is cross-platform, uses a standard format for email message storage, and uses an open format for its address book. I'm using Mac OS X now, but it's possible that I might want to switch to Linux (Ubuntu is quite nice) or be forced to switch to Windows at some point. I can use Thunderbird on those platforms, too.

When I must open Word documents, Excel spreadsheets, and PowerPoint presentations I use NeoOffice. On Windows, I use OpenOffice.org.

It's most important to me that my data is portable not only between operating systems but also between computers and applications. I refuse to get locked down into any proprietary format or have my data locked down on only one computer. I'm letting Google store my (encrypted) browser information, but I have my own copies of the data on more than one computer, inside Firefox itself.

Saturday, June 9, 2007

Feeling Left Behind

A few months ago, a study surfaced on Reddit that stated that the ability to digest milk in human adults is a relatively recent adaptation. It further posited that this ability was convincing proof of the theory of evolution: humans have evolved to be able to digest lactose later in life.

Years and years ago, I remember reading one of those fun kids' science articles that talked about genetic traits like the ability to roll your tongue or wiggle your ears. The one that stuck with me was that some people have a small, solitary hair between the last two knuckles of the ring fingers on either hand. I always imagined that having that hair proved that you were a genetic throwback.

I have that hair. I'm also slightly lactose intolerant. I feel like a caveman. Maybe I could get a job acting in a Geiko commercial.

At least I have some compensations: I can roll my tongue and wiggle my ears. So there, all you evolved monkeys!

Tuesday, June 5, 2007

Speedier Erlang

In the comments to Erlang Fractal Benchmark, Ulf Wiger noted in the comments that adding is_float guard clauses speeds up the code.

When I asked why on the Erlang email list, Ulf and others explained: when the compiler sees is_float(X) it knows that X must be a float. Instead of worrying about type checks, casting, and other inefficiencies, it can optimize the code that uses X.

Monday, June 4, 2007

Erlang Fractal Benchmark

While looking at a simple fractal benchmark that showed up on the programming Reddit, I noticed that there wasn't an Erlang version. Below is one I wrote last night. Erlang fares rather well. One thing mildly surprised me: it runs slightly faster in an Erlang shell within Emacs than in both Apple's Terminal and iTerm on Mac OS X. Within Emacs it runs in 1.09000 (runtime) 1.14100 (wall clock) seconds. In both Terminal and iTerm it runs in around 1.11000 (runtime) 1.16600 (wall clock) seconds. Perhaps screen I/O isn't as fast in the terminal programs.

Two caveats: first, these numbers were generated on my 2.33 GHz Intel MacBook Pro; I don't know what the original benchmarks used. Also, I only ran the code a handful of times and picked a "typical" time to report. A better test would have been to run the code hundreds or thousands of times and average the values.

This post also says a bit about intuition vs. measuring. I discuss some code modifications and their expected and actual effects below.

Another thing to note: the author of the fractal benchmark page says that he hasn't bothered to optimize the code for each language he tested. I don't know if using lists:map/2 or extracting iter_value/5 and using guard clauses would disqualify this version in his opinion.

-module(fractal_benchmark).
-author("Jim Menard, jimm@io.com").
-export([run/0]).

-define(BAILOUT, 16).
-define(MAX_ITERATIONS, 1000).

%% Idea from http://www.timestretch.com/FractalBenchmark.html

run() ->
    io:format("Rendering~n"),
    statistics(runtime),
    statistics(wall_clock),
    lists:map(fun(Y) ->
                      io:format("~n"),
                      lists:map(fun(X) -> plot(X, Y) end, lists:seq(-39, 39))
              end,
              lists:seq(-39, 39)),
    io:format("~n"),
    {_, Time1} = statistics(runtime),
    {_, Time2} = statistics(wall_clock),
    Sec1 = Time1 / 1000.0,
    Sec2 = Time2 / 1000.0,
    io:format("Erlang Elapsed ~p (runtime) ~p (wall clock) seconds~n",
              [Sec1, Sec2]).

plot(X, Y) ->
    case iterate(X/40.0, Y/40.0) of
        0 ->
            io:format("*");
        _ ->
            io:format(" ")
    end.

iterate(X, Y) ->
    CR = Y - 0.5,
    CI = X,
    iter_value(CR, CI, 0.0, 0.0, 0).

iter_value(_, _, _, _, I) when I > ?MAX_ITERATIONS ->
    0;
iter_value(_, _, ZI, ZR, I) when ZI * ZI + ZR * ZR > ?BAILOUT ->
    I;
iter_value(CR, CI, ZI, ZR, I) ->
    Temp = ZR * ZI,
    ZR2 = ZR * ZR,
    ZI2 = ZI * ZI,
    ZRnew = ZR2 - ZI2 + CR,
    ZInew = Temp + Temp + CI,
    iter_value(CR, CI, ZInew, ZRnew, I + 1).

You might have noticed that ZI * ZI and ZR * ZR are calculated twice: once in the body of the last clause and once in the second guard clause. The guard clause has to be executed every time, which means that running the last, most frequently executed clause executes the multiplications twice. I tried pre-calculating those values and adding them as parameters, so the arg list is

iter_value(_CR, _CI, _ZI, _ZI2, _ZR, _ZR2, I)

Did it help? It did indeed. Execution time in Emacs went down to 0.890000 (runtime) 0.919000 (wall clock) seconds. Here are the modified versions of iterate and iter_value:

iterate(X, Y) ->
    CR = Y - 0.5,
    CI = X,
    iter_value(CR, CI, 0.0, 0.0, 0.0, 0.0, 0).

iter_value(_, _, _, _, _, _, I) when I > ?MAX_ITERATIONS ->
    0;
iter_value(_, _, _, ZI2, _, ZR2, I) when ZI2 + ZR2 > ?BAILOUT ->
    I;
iter_value(CR, CI, ZI, ZI2, ZR, ZR2, I) ->
    Temp = ZR * ZI,
    ZRnew = ZR2 - ZI2 + CR,
    ZInew = Temp + Temp + CI,
    iter_value(CR, CI, ZInew, ZInew * ZInew, ZRnew, ZRnew * ZRnew, I + 1).

One final thing I tried was commenting out the calls to io:format. In my experience, screen I/O usually slows things down quite a bit. (In the case statement in plot/2, I had to replace them with void statements instead of simply commenting them out.) The result: execution time went down to 0.830000 (runtime) 0.839000 (wall clock) seconds within Emacs. In iTerm, execution time was only slightly slower than that. So the time decreased, but not nearly as much as it did when I removed the extra multiplications. I'm surprised. Is multiplication that expensive in Erlang, or is I/O well optimized, or was my instinct wrong? Come to think of it, io:format is only called once per coordinate; the multiplications happen thousands of times for each.

A distributed version of this algorithm is certainly possible (say, one process for every X,Y coordinate). My gut tells me that it would run slower because the calculation for an individual coordinate is relatively small and the message passing overhead and gathering and coordination of the results would outweigh the benefits. My intuition was wrong about the effects of I/O, though. I'd have to try it to make sure.

Additions: In the comments, Ulf Wiger suggested that I add is_float guards for all the function parameters that are floats. Doing this to iter_value/7 reduced execution time by almost half to 0.450000 (runtime) 0.455000 (wall clock) seconds, a huge savings. (Does anybody know why adding these guard clauses speeds up execution? I would imaging that extra checking would slow it down.) Here's the new code for iter_value/7:

iter_value(_CR, _, _ZI, _ZI2, _ZR, _ZR2, I)
  when I > ?MAX_ITERATIONS,
  is_float(_CR), is_float(_ZI), is_float(_ZI2), is_float(_ZR), is_float(_ZR2) ->
    0;
iter_value(_CR, _, _ZI, _ZI2, _ZR, _ZR2, I)
  when _ZI2 + _ZR2 > ?BAILOUT,
  is_float(_CR), is_float(_ZI), is_float(_ZI2), is_float(_ZR), is_float(_ZR2) ->
    I;
iter_value(CR, CI, ZI, ZI2, ZR, ZR2, I)
  when
  is_float(CR), is_float(ZI), is_float(ZI2), is_float(ZR), is_float(ZR2) ->
    Temp = ZR * ZI,
    ZRnew = ZR2 - ZI2 + CR,
    ZInew = Temp + Temp + CI,
    iter_value(CR, CI, ZInew, ZInew * ZInew, ZRnew, ZRnew * ZRnew, I + 1).

Ulf and others also suggested that I avoid printing each character separately. Doing so did not seem to change execution time at all. Here's what I did:

% changed the inner map call to plot
run() ->
    % ...
    Seq = lists:seq(-39, 39),
    lists:map(fun(Y) ->
                      CharList = lists:map(fun(X) -> plot(X, Y) end, Seq),
                      io:format("~s~n", [CharList])
              end,
              Seq),
    % ...

% changed plot to return single-character strings
plot(X, Y) ->
    case iterate(X/40.0, Y/40.0) of
        0 ->
            "*";
        _ ->
            " "
    end.

I also tried commenting out the io:format/2 call above. Execution time went up about 1/100 of a second.

Tuesday, May 22, 2007

An Erlang MIDI File Reader/Writer

I've been learning Erlang. (See Learning a New Programming Language.) As part of that process, I've written three programs so far: a Boids flock simulation, a simple Web application using Erlyweb, and a MIDI file reader/writer.

One point that I might not have made clearly in my previous post is the reasons that I learn a new language: to learn new ways to think about and solve coding problems, to see what's out there that's better than what I'm using (I try not to be a Blub programmer), to keep my brain sharp, and because it's fun.

Coding the MIDI file library helped me learn about Erlang's file I/O, the binary data type, and a bit more about pattern matching. (The Boids simulation has much more pattern matching goodness; more about that in another post.) The code isn't distributed, nor does it really take advantage of many of Erlang's strengths such as distributed process (Boids does). It does make use of the binary data type and pattern matching quite heavily.

I was also forced to think about data representation in Erlang: how would I represent a sequence, a track, and a single MIDI event? I'm not convinced that I have come up with the best representations, because I have not yet had the time to use this library for any MIDI file manipulation. When I get to that point, I fully expect that the data representation will change.

Having written this code in a few different languages before, I knew where to start: with the easy stuff! I first defined the constants. midi_consts.hrl contains all of the define statements for all the constants I might need. It was while creating this file that I learned how to write hex numbers in Erlang. (That sounds like a small, silly thing but I was quite annoyed for a few minutes before I figure out how to do that.)

% Channel messages
-define(STATUS_NIBBLE_OFF, 16#8).
-define(STATUS_NIBBLE_ON, 16#9).
% ...
% System common messages
-define(STATUS_SYSEX, 16#F0).
-define(STATUS_SONG_POINTER, 16#F2).
% ...

Next, I dove into reading a MIDI file. Since I only deal with MIDI type 1 files which contain multiple tracks in a single file, that's all I bothered writing. I knew I'd have to read the header and one or more tracks. Erlang code tends to use atoms, tuples, lists, and records to represent many types of data, so I came up with a proposed data format for my sequence data. From the comment for midifile:read/1, which takes the path to a MIDI file and returns a seq tuple:

%% Returns
%%   {seq, {header...}, ListOfTracks}
%% header is {header, Format, Division}
%% each track is
%%   {track, ListOfEvents}
%% each event is
%%   {event_name, DeltaTime, [values...]}
%% where values after DeltaTime are specific to each event type.
%% If the value is a string, then the string appears instead of
## [values...].

After writing some code that read one byte at a time, I went back to the "Programming with Files" chapter in Programming Erlang by Joe Armstrong and realized that I could slurp all of the MIDI data into memory and randomly access it. This made my code cleaner and faster. It also made pattern matching much easier, because I could write midifile:read_event with many different pattern-matched arguments but the same arity. Here's the code that reads an event list within a track:

event_list(_F, _FilePos, 0) ->
    [];
event_list(F, FilePos, BytesToRead) ->
    [DeltaTime, VarLenBytesUsed] = read_var_len(file:pread(F, FilePos, 4)),
    {ok, ThreeBytes} = file:pread(F, FilePos+VarLenBytesUsed, 3),
    ?DPRINT("reading event, FilePos = ~p, BytesToRead = ~p, ThreeBytes = ~p~n",
     [FilePos, BytesToRead, ThreeBytes]),
    [Event, EventBytesRead] =
 read_event(F, FilePos+VarLenBytesUsed, DeltaTime, ThreeBytes),
    BytesRead = VarLenBytesUsed + EventBytesRead,
    [Event | event_list(F, FilePos + BytesRead, BytesToRead - BytesRead)].

Let's start at the end: event_list/3 is recursive. It builds the list of events by reading a single event, then calling itself with the remaining bytes to read in the track. If the remaning number of bytes is 0, the first clause (the first two lines above) matches and an empty list is returned. The second, longer clause starts by reading a variable length integer (more about that below). It then reads the next three bytes of the file (again, randomly accessing an in-memory copy of the file). Why three bytes? Because most MIDI events are two or three bytes long. If I need fewer bytes, no harm done. If I need more, I read more. Those three bytes are passed on to read_event/4, which is a function that is made up of a really long series of clauses, each of which matches a different MIDI event.

Here are the first few read_event/4 clauses. The first three arguments are the file, current file position, and delta time of the event. These clauses all return an array consisting of the event tuple and the number of bytes used by the event (excepting the length of the delta time).

read_event(_F, _FilePos, DeltaTime,
    <<?STATUS_NIBBLE_OFF:4, Chan:4, Note:8, Vel:8>>) ->
    ?DPRINT("off~n", []),
    put(status, ?STATUS_NIBBLE_OFF),
    put(chan, Chan),
    [{off, DeltaTime, [Chan, Note, Vel]}, 3];
% note on, velocity 0 is a note off
read_event(_F, _FilePos, DeltaTime,
    <<?STATUS_NIBBLE_ON:4, Chan:4, Note:8, 0:8>>) ->
    ?DPRINT("off (using on vel 0)~n", []),
    put(status, ?STATUS_NIBBLE_ON),
    put(chan, Chan),
    [{off, DeltaTime, [Chan, Note, 64]}, 3];
read_event(_F, _FilePos, DeltaTime,
    <<?STATUS_NIBBLE_ON:4, Chan:4, Note:8, Vel:8>>) ->
    ?DPRINT("on~n", []),
    put(status, ?STATUS_NIBBLE_ON),
    put(chan, Chan),
    [{on, DeltaTime, [Chan, Note, Vel]}, 3];
read_event(_F, _FilePos, DeltaTime,
    <<?STATUS_NIBBLE_POLY_PRESS:4, Chan:4, Note:8, Amount:8>>) ->
    ?DPRINT("poly press~n", []),
    put(status, ?STATUS_NIBBLE_POLY_PRESS),
    put(chan, Chan),
    [{poly_press, DeltaTime, [Chan, Note, Amount]}, 3];
read_event(_F, _FilePos, DeltaTime,
    <<?STATUS_NIBBLE_CONTROLLER:4, Chan:4, Controller:8, Value:8>>) ->
    ?DPRINT("controller ch ~p, ctrl ~p, val ~p~n", [Chan, Controller, Value]),
    put(status, ?STATUS_NIBBLE_CONTROLLER),
    put(chan, Chan),
    [{controller, DeltaTime, [Chan, Controller, Value]}, 3];

The put calls store the status and channel in the process dictionary, one of the few places in Erlang that you can modify values. To handle running status bytes, we need to remember the status and channel. I picked the process dictionary as the place to store that.

Added 2007-05-23: "Running status bytes" are a way to reduce the size of MIDI files. If the status byte is the same as the previous status byte, you can omit it. Also, as a special case a note-on value with a velocity of zero is considered to be a note-off message. I needed code that would recognize that the next byte was not a status byte, and use the previous status byte. I made that an additional read_event/4 clause, like this:

% Handle running status bytes
read_event(F, FilePos, DeltaTime, <<B0:8, B1:8, _:8>>) when B0 < 128 ->
    Status = get(status),
    Chan = get(chan),
    ?DPRINT("running status byte, status = ~p, chan = ~p~n", [Status, Chan]),
    [Event, NumBytes] =
 read_event(F, FilePos, DeltaTime, <<Status:4, Chan:4, B0:8, B1:8>>),
    [Event, NumBytes - 1];

We read the status and channel from the process dictionary and pass them back to read_event/4, which will use Erlang's pattern matching to go back and find the proper code for that status byte. End of additions.

?DPRINT is a macro that I defined to output when DEBUG is defined and do nothing when it isn't:

% -define(DEBUG, true).
-ifdef(DEBUG).
-define(DPRINT(X, Y), io:format(X, Y)).
-else.
-define(DPRINT(X, Y), void).
-endif.

MIDI encodes certain multi-byte integer values by "variable length encoding" it: splitting it into seven bit chunks and outputting them with the highest-order chunk first. The last, lowest-bit chunk has the high bit set to zero, the earlier, higher chunks have the high bit set to one. Thus zero is encoded as 00000000 and 129 is encoded as 10000001 00000001. Notice that the code in event_list/1 always reads the next four bytes; that's because the MIDI spec guarantees that var length numbers are at most four bytes long. Here's the code for read_var_len/1, which returns a list containing the value and the number of bytes it uses:

read_var_len({ok, <<0:1, B0:7, _:24>>}) ->
    [B0, 1];
read_var_len({ok, <<1:1, B0:7, 0:1, B1:7, _:16>>}) ->
    [(B0 bsl 7) + B1, 2];
read_var_len({ok, <<1:1, B0:7, 1:1, B1:7, 0:1, B2:7, _:8>>}) ->
    [(B0 bsl 14) + (B1 bsl 7) + B2, 3];
read_var_len({ok, <<1:1, B0:7, 1:1, B1:7, 1:1, B2:7, 0:1, B3:7>>}) ->
    [(B0 bsl 21) + (B1 bsl 14) + (B2 bsl 7) + B3, 4];
read_var_len({ok, <<1:1, B0:7, 1:1, B1:7, 1:1, B2:7, 1:1, B3:7>>}) ->
    ?DPRINT("WARNING: bad var len format; all 4 bytes have high bit set~n", []),
    [(B0 bsl 21) + (B1 bsl 14) + (B2 bsl 7) + B3, 4].

bsl means "bit-shift left".

Only after finishing the MIDI file reader code did I attack the MIDI file writer. For that, I created an in-memory "IO list". An IO list is a list that consts of other IO lists, binaries, or bytes (integers between 0 and 155). The Erlang library method file:write_file/2 writes an IO list with one call.

Here is the code that writes a MIDI var len value:

var_len(I) when I < (1 bsl 7) ->
    <<0:1, I:7>>;
var_len(I) when I < (1 bsl 14) ->
    <<1:1, (I bsr 7):7, 0:1, I:7>>;
var_len(I) when I < (1 bsl 21) ->
    <<1:1, (I bsr 14):7, 1:1, (I bsr 7):7, 0:1, I:7>>;
var_len(I) when I < (1 bsl 28) ->
    <<1:1, (I bsr 21):7, 1:1, (I bsr 14):7, 1:1, (I bsr 7):7, 0:1, I:7>>;
var_len(I) ->
    exit("Value " ++ I ++ " is too big for a variable length number").

This code makes use of guard clauses: expressions that determine whether or not to execute a function clause. Note that the final clause exits. This matches Erlang's philosophy that you should code for the normal case and let errors happen. It might have been more in the Erlang style to leave off the last case entirely, and let the program fail with a "bad match". Perhaps I'll remove it.

After finishing the writer, I read in a file with the reader and output the Erlang representation using the writer. The result: a few differences, but all due to decisions about running status byte values. The code isn't perfect; it has a long way to go. I've learned a lot, though.

Next: a Boids simulation in Erlang.

Learning a New Programming Language

I'm a self-professed language maven. I love learning new programming languages. Recently, I've been learning Erlang, thanks to Programming Erlang by Joe Armstrong, the online documentation, and the Erlang mailing list.

I'm a hands-on learner (see Just Try It). For me, there are a few phases to learning a new language:

  • Overview
  • Installation and Documentation
  • Syntax
  • Standard libraries
  • Philosophy
  • Frameworks

These phases don't occur sequentially; they blur together. For example, when trying to absorb what I call the philosophy of a language, I'll be writing code that forces me to look for how to use the standard libraries and look for existing frameworks.

The overview phase is when I initially hear about a language. It might be through posts in mailing lists or discussions groups, the various RSS feeds to which I subscribe, or on Reddit. During this phase I tend to form a few first impressions about a language: is it really different? How? Why does it exist? Will it be useful to me in my day-to-day work? Is it worth learning to exercise my brain, or because I will learn new concepts?

Next comes installation and documentation. I grab a copy of the language and whatever documentation I can. This is a key step: if I can't install the language or get it running, or if the documentation doesn't give me enough information to get started, then I'm done.

Learning the syntax gets me as far as being able to follow along with a book or the documentation. This is also another place for me to be turned off by a language. For example, when I started playing with Python, I decided it wasn't for me for a few reasons: significant whitespace, the __funky__ naming convention for magic OO methods, and having to refer to "self" all the time. Please remember, Pythonistas (all one of you that might read this :-), that I'm talking about my impressions and my decisions, not yours or anybody else's.

I'm not sure what made me temporarily abandon Scala (see Scala Baby-Steps) for Erlang. Partly, I was seeing references to Erlang when reading about Scala. Partly, it felt a bit less developed and complete then Erlang. Mostly, I think I'm learning more new things with Erlang.

Becoming familiar with a language's standard libraries or classes lets me know what comes "out of the box", as opposed to what I'll have to go look for or build for myself. It also gives me a good sense of the language's approach to solving various kinds of problems, large and small, and at different levels (from one-liners like iterating through a list through larger libraries like parsers or network communications). Again, the libraries can be make-or-break for me: while playing with Python, I found that simple string and regular expression operations were too difficult to find. (Actually, this is a problem with Erlang, too: string operations are all over the place in different libraries. One place for making a string upper case, another for making it lower case.)

By philosophy I mean a few different things: what the language is best at (and worst at), what it was designed for, what its strengths and weaknesses are, and what good code in the language looks like. Philosophy often, but not always, goes hand-in-hand with learning the syntax and the standard libraries.

During this phase, I usually pick for myself a small number of decent-sized tasks. By this time, I've been writing code in the new language for a while, but mostly toy exercises. It's time to write a larger program. Usually I take a project I've already written in a few different languages and port it to the new language. The code starts as a straight port, but as I progress I try to rewrite it using the idioms and techniques offered by the new language.

By this time, I've usually look for and found a few different language frameworks on the order of as a Web application server or app development framework. This gives me an idea not only of what's available, but what the community is like.

Speaking of community, by this time I've probably joined the language's primary mailing list.

I plan to blog about three different projects I've been working on in Erlang: a MIDI file reader/writer, a Boids simulation, and a simple Web application using Erlyweb.

Tuesday, January 9, 2007

Scala Baby-Steps

I'm starting to learn Scala. It's a statically- and strongly-typed functional language that compiles to Java byte code and integrates well with Java.

One of the things I'm struggling with is the language's strong static typing. Scala is nice in that it tries very hard to infer types. That means the programmer doesn't spend much time typing "wasteful" type declarations. If Scala didn't have that, I would have run away screaming already.

I've figured out how to connect to a MySQL database. There are a few database access methods that are being developed in Scala. The one that ships with the language is called "dbc". It comes with a PostgreSQL connection interface but no MySQL version. Here's my MySQL version along with some simple code that performs a select. (I'd move the connection information into a properties file if I were to really use this code.)

import scala.dbc._
import scala.dbc.Syntax._
import scala.dbc.syntax.Statement._
import java.net.URI

object MysqlVendor extends Vendor {
  val uri = new URI("jdbc:mysql://localhost:3306/my_database_name")
  val user = "my_database_user"
  val pass = "my_database_password"
  
  val retainedConnections = 5
  val nativeDriverClass = Class.forName("com.mysql.jdbc.Driver")
  val urlProtocolString = "jdbc:mysql:"
}

object BulkUploadRunner extends Application {
  val db = new Database(MysqlVendor)

  val rows = db.executeStatement {
    select fields ("email_id" of characterVarying(32)) from ("bulk_uploads")
  }
  for (val r <- rows;
       val f <- r.fields) {
    Console.println(f.content.nativeValue) // or .sqlValue
  }

  db.close
}
Link