Posterous theme by Cory Watilo

When choice of programming environment does matter

The standard advice given to the question "What programming language should I use for my next big project?" has always been "whatever you're most familiar with." I'm not in violent disagreement with that sentiment, as long as you're using tools that don't get in your way.

I've been writing .NET code in the corporate world for several years now and although I am probably more familiar with this stack than Rails, I'd never use .NET with any of my own projects. It gets in my way. All the damn time.

Here's an example. Last week I started up Visual Studio and it crashed with some strange nonsense. I restarted it, the usual cure for most Windows issues. No dice. I googled the specific error, found a command line switch to start it in Safe Mode. It would start up, but I had a new random-crashing problem. I checked some logs, googled around a bit more, tried more advice. I ended up install a service pack, uninstalling it, installing it again, attempting a "repair" (which was essentially a re-install), and eventually had to settle for downloading a full DVD iso and completely uninstalling Visual Studio. It took me two full work days to get things back to where they were between reboots and downloads. The install process alone takes close to two hours.

How much time would I have wasted on my Mac at home? Next to nothing. I've never had any issues with Emacs. It's just a (kitchen sink) text editor. If I had to re-install it, it wouldn't take two hours. And it would certainly not require a reboot.

Sure, having to do a full re-install of the IDE might be a semi-rare occurrence. I've been using Emacs for years and I've never needed to reinstall it. Same with Ruby, Python, anything I've ever used. This recent re-install isn't the only thing that's ever gotten in my way. I restart the IDE several times throughout the day in response to lockups and crashes. I run into bugs quite often where the debugger will simply skip my breakpoints, the shortcut for stepping into a function just skips it entirely, or where I can't even inspect a variable because "function evaluation has timed out."

If you're spending time on stuff like this instead of launching your startup, curing cancer, or, hell -- spending time with your family, maybe you should just take a look at what else is out there.

I'm sure there are probably people out there who have never known what it's like to work with stable tools that get the job done. If your tools get in the way, I encourage you to give something else a chance if you value your time and want programming to stay fun. I promise you won't regret it.

A simple Erlang IRC bot

I've been experimenting with Erlang for a while now, and it's been an experience. I've had a little rougher time with it than most people, but most of the blogs I've been reading are written by exceptional or brilliant programmers, so I'm not too concerned :). I went ahead and wrote myself a little Erlang IRC bot, as I had a hard time finding one that I could extend that isn't 5 years old and way too big or doesn't even work when installed using apt-get install (not to mention I couldn't find its source, either).

Here's what I came up with:

-module(bot).
-author("jonathan.roes@gmail.com").
-export([connect/2, loop/1]).
-define(nickname, "jroes-test").
-define(channel, "#jroes-test").

% Connect to an IRC server with a given Host and Port.  Set up the TCP option to
% give us messages on a line-by-line basis.
connect(Host, Port) ->
        {ok, Sock} = gen_tcp:connect(Host, Port, [{packet, line}]),
        % According to RFC1459, we need to tell the server our nickname and username
        gen_tcp:send(Sock, "NICK " ++ ?nickname ++ "\r\n"),
        gen_tcp:send(Sock, "USER " ++ ?nickname ++ " blah blah blah blah\r\n"),
        loop(Sock).
        
% Now that we're connected, receive TCP messages and parse them.
loop(Sock) ->
        receive
                {tcp, Sock, Data} ->
                        io:format("[~w] Received: ~s", [Sock, Data]),
                        parse_line(Sock, string:tokens(Data, ": ")),
                        loop(Sock);
                quit ->
                        io:format("[~w] Received quit message, exiting...~n", [Sock]),
                        gen_tcp:close(Sock),
                        exit(stopped)
        end.

% The following is an example of the message this fun intends to parse.  Here we see
% the limitation that tokenizing the string on both :'s and spaces puts on us.
% [#Port<0.124>] Received: :jroes!jroes@mask-2EDB8BDB.net PRIVMSG #jroes-test :jroes-test: wassup?
parse_line(Sock, [User,"PRIVMSG",Channel,?nickname|_]) ->
        Nick = lists:nth(1, string:tokens(User, "!")),
        irc_privmsg(Sock, Channel, "You talkin to me, " ++ Nick ++ "?");
        
% If the second token is "376", then join our channel.  376 indicates End of MOTD.
parse_line(Sock, [_,"376"|_]) ->
        gen_tcp:send(Sock, "JOIN :" ++ ?channel ++ "\r\n");

% The server will periodically send PINGs and expect you to PONG back to make sure
% you haven't lost the connection.
parse_line(Sock, ["PING"|Rest]) ->
        gen_tcp:send(Sock, "PONG " ++ Rest ++ "\r\n");

parse_line(_, _) ->
        0.

% This just helps us write a PRIVMSG back to a client without having to type
% the newlines and :'s ourselves so much.  It'll be more useful later.
irc_privmsg(Sock, To, Message) ->
        gen_tcp:send(Sock, "PRIVMSG " ++ To ++ " :" ++ Message ++ "\r\n").

To run and play with:

jroes@halcyon:~/src$ wget http://jroes.net/bot.erl
jroes@halcyon:~/src$ erl
1> c("bot.erl").
{ok,bot}
2> Bot = spawn(bot, connect, ["irc.server.com", 6667]).
3> Bot ! quit.

Note: This post was migrated over from my old blog as some people have asked for it in the past.